diff --git a/.github/workflows/update-specs.yml b/.github/workflows/update-specs.yml index d9459ad..26d593c 100644 --- a/.github/workflows/update-specs.yml +++ b/.github/workflows/update-specs.yml @@ -28,6 +28,6 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add api/ api-reference/ docs.json + git add api/ payments/api-reference/ docs.json git commit -m "chore: update OpenAPI specs" git push diff --git a/apps/javascript/installation.mdx b/apps/javascript/installation.mdx new file mode 100644 index 0000000..a309bb3 --- /dev/null +++ b/apps/javascript/installation.mdx @@ -0,0 +1,472 @@ +--- +title: Installation +sidebarTitle: JavaScript +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app). + + + +### 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 +npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Yarn +yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Bun +bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash pnpm +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 +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 +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 +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 +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 +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 + +.... + +
+ +
+ +``` + +## Smart Contract Interaction + + + + +[Wagmi hooks](https://wagmi.sh/react/api/hooks/useReadContract) can help us interact with wallets and smart contracts: + +```tsx +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 +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 +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}`); +} +``` + + + diff --git a/apps/next/installation.mdx b/apps/next/installation.mdx new file mode 100644 index 0000000..1fb2638 --- /dev/null +++ b/apps/next/installation.mdx @@ -0,0 +1,469 @@ +--- +title: Installation +sidebarTitle: Next.js +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app). + + + +### 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 +npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Yarn +yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Bun +bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash pnpm +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 +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 +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 +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 +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 +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 + // 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 +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 +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 +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}`); +} +``` + + + diff --git a/apps/overview.mdx b/apps/overview.mdx new file mode 100644 index 0000000..af06f5e --- /dev/null +++ b/apps/overview.mdx @@ -0,0 +1,130 @@ +--- +title: "WalletConnect for Apps" +sidebarTitle: 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. \ No newline at end of file diff --git a/apps/react/installation.mdx b/apps/react/installation.mdx new file mode 100644 index 0000000..035da2b --- /dev/null +++ b/apps/react/installation.mdx @@ -0,0 +1,483 @@ +--- +title: Installation +sidebarTitle: React +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app). + + + +### 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 +npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Yarn +yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Bun +bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash pnpm +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 +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 +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 +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 +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 +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 + // 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 +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 +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 +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}`); +} +``` + + + diff --git a/apps/vue/installation.mdx b/apps/vue/installation.mdx new file mode 100644 index 0000000..7a2cd38 --- /dev/null +++ b/apps/vue/installation.mdx @@ -0,0 +1,487 @@ +--- +title: Installation +sidebarTitle: Vue +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app). + + + +### 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 +npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Yarn +yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash Bun +bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers +``` + +```bash pnpm +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 +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 +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 +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 +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 + +``` + + +## Trigger the modal + +To open the WalletConnect modal you need to call the `connect` function from the Universal Connector. + +```tsx + + +... + +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 + +``` + +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 + +``` + + + + + [@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 +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 + }; + } +}; +``` + + + diff --git a/docs.json b/docs.json index 980b937..f296086 100644 --- a/docs.json +++ b/docs.json @@ -8,17 +8,145 @@ "dark": "#0988f0" }, "redirects": [ - { - "source": "/guides/**", - "destination": "/custodians/**" - }, { "source": "/payments/merchant/onboarding", "destination": "/payments/merchant/quickstart" }, { - "source": "/api-reference/latest/:slug*", - "destination": "/api-reference/2026-02-18/:slug*" + "source": "/api-reference/:slug*", + "destination": "/payments/api-reference/:slug*" + }, + { + "source": "/payments/api-reference/latest/:slug*", + "destination": "/payments/api-reference/2026-02-18/:slug*" + }, + { + "source": "/wallet-sdk/chain-support/:slug*", + "destination": "/wallets/chains/:slug*" + }, + { + "source": "/wallet-sdk/best-practices", + "destination": "/wallets/more/best-practices" + }, + { + "source": "/wallet-sdk/upgrade/staying-up-to-date", + "destination": "/wallets/more/updating-wallet-sdk" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-to-reown", + "destination": "/wallets/more/web3wallet-migration/quickstart" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-web", + "destination": "/wallets/more/web3wallet-migration/web" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-react-native", + "destination": "/wallets/more/web3wallet-migration/react-native" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-flutter", + "destination": "/wallets/more/web3wallet-migration/flutter" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-android", + "destination": "/wallets/more/web3wallet-migration/android" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-ios", + "destination": "/wallets/more/web3wallet-migration/ios" + }, + { + "source": "/wallet-sdk/upgrade/from-web3wallet-unity", + "destination": "/wallets/more/web3wallet-migration/unity" + }, + { + "source": "/wallet-sdk/:slug*", + "destination": "/wallets/:slug*" + }, + { + "source": "/app-sdk/:slug*", + "destination": "/apps/:slug*" + }, + { + "source": "/guides/:slug*", + "destination": "/wallets/guides/:slug*" + }, + { + "source": "/custodians/:slug*", + "destination": "/wallets/custodians/:slug*" + }, + { + "source": "/walletguide/explorer-submission", + "destination": "/wallets/walletguide/submit-wallet" + }, + { + "source": "/walletguide/explorer", + "destination": "/wallets/walletguide/explorer-api" + }, + { + "source": "/walletguide/chains/overview", + "destination": "/wallets/walletguide/submit-chain" + }, + { + "source": "/walletguide/chains/chain-list", + "destination": "/wallets/walletguide/chain-list" + }, + { + "source": "/walletguide/wallets/wallet-list", + "destination": "/wallets/walletguide/wallet-list" + }, + { + "source": "/walletguide/:slug*", + "destination": "/wallets/walletguide/:slug*" + }, + { + "source": "/token-dynamics/intro", + "destination": "/network/WCT-token/overview" + }, + { + "source": "/token-dynamics/fees", + "destination": "/network/WCT-token/overview" + }, + { + "source": "/token-dynamics/wallet-rewards", + "destination": "/network/WCT-token/rewards" + }, + { + "source": "/token-dynamics/rewards", + "destination": "/network/WCT-token/rewards" + }, + { + "source": "/token-dynamics/service-node-rewards", + "destination": "/network/WCT-token/service-node-rewards" + }, + { + "source": "/wct-staking/overview", + "destination": "/network/WCT-token/staking" + }, + { + "source": "/wct-staking/faq", + "destination": "/network/WCT-token/faq" + }, + { + "source": "/governance", + "destination": "/network/WCT-token/governance" + }, + { + "source": "/contracts", + "destination": "/network/WCT-token/contracts" + }, + { + "source": "/service-nodes", + "destination": "/network/service-nodes" + }, + { + "source": "/wallets", + "destination": "/network/wallets" + }, + { + "source": "/overview", + "destination": "/network" } ], "integrations": { @@ -30,37 +158,363 @@ "favicon": "/favicon.ico", "navigation": { "tabs": [ + { + "tab": "Wallets", + "icon": "wallet", + "dropdowns": [ + { + "dropdown": "Wallet SDK - Overview", + "icon": "wallet", + "pages": [ + { + "group": "Getting Started", + "pages": [ + "wallets/overview" + ] + }, + { + "group": "Features", + "pages": [ + "wallets/features/verify", + "wallets/features/link-mode", + "wallets/features/one-click-auth" + ] + }, + { + "group": "Chain Support", + "pages": [ + "wallets/chains/overview", + "wallets/chains/evm", + "wallets/chains/solana", + "wallets/chains/bitcoin", + "wallets/chains/sui", + "wallets/chains/stacks", + "wallets/chains/ton", + "wallets/chains/tron", + "wallets/chains/adi", + "wallets/chains/canton", + "wallets/chains/stellar" + ] + }, + { + "group": "WalletGuide", + "pages": [ + "wallets/walletguide/submit-wallet", + "wallets/walletguide/wallet-list", + "wallets/walletguide/submit-chain", + "wallets/walletguide/chain-list", + "wallets/walletguide/explorer-api" + ] + }, + { + "group": "More", + "pages": [ + "wallets/more/best-practices", + "wallets/more/updating-wallet-sdk", + { + "group": "Web3Wallet to Wallet SDK", + "pages": [ + "wallets/more/web3wallet-migration/quickstart", + "wallets/more/web3wallet-migration/web", + "wallets/more/web3wallet-migration/react-native", + "wallets/more/web3wallet-migration/flutter", + "wallets/more/web3wallet-migration/android", + "wallets/more/web3wallet-migration/ios", + "wallets/more/web3wallet-migration/unity" + ] + } + ] + }, + { + "group": "Custodians & Institutions", + "pages": [ + "wallets/custodians/overview", + "wallets/custodians/app-access-control", + "wallets/custodians/contract-access-control", + "wallets/custodians/extended-sessions" + ] + } + ] + }, + { + "dropdown": "Web", + "icon": "js", + "description": "Wallet SDK on Web", + "pages": [ + "wallets/web/installation", + "wallets/web/usage", + "wallets/web/one-click-auth", + "wallets/web/verify", + "wallets/web/eip5792", + "wallets/web/chain-abstraction", + "wallets/web/best-practices", + "wallets/web/resources", + { + "group": "Cloud", + "pages": [ + "wallets/web/cloud/explorer-submission", + "wallets/web/cloud/relay", + "wallets/web/cloud/analytics" + ] + } + ] + }, + { + "dropdown": "Android", + "icon": "android", + "description": "Wallet SDK on Android", + "pages": [ + { + "group": "Core", + "pages": [ + "wallets/android/installation", + "wallets/android/usage", + "wallets/android/one-click-auth", + "wallets/android/mobile-linking", + "wallets/android/link-mode", + "wallets/android/verify", + "wallets/android/eip5792", + "wallets/android/chain-abstraction", + "wallets/android/best-practices", + "wallets/android/resources" + ] + }, + { + "group": "Cloud", + "pages": [ + "wallets/android/cloud/explorer-submission", + "wallets/android/cloud/relay", + "wallets/android/cloud/analytics" + ] + } + ] + }, + { + "dropdown": "iOS", + "icon": "apple", + "description": "Wallet SDK on iOS", + "pages": [ + { + "group": "Core", + "pages": [ + "wallets/ios/installation", + "wallets/ios/usage", + "wallets/ios/one-click-auth", + "wallets/ios/mobile-linking", + "wallets/ios/link-mode", + "wallets/ios/verify", + "wallets/ios/eip5792", + "wallets/ios/chain-abstraction", + "wallets/ios/best-practices", + "wallets/ios/resources" + ] + }, + { + "group": "Cloud", + "pages": [ + "wallets/ios/cloud/explorer-submission", + "wallets/ios/cloud/relay", + "wallets/ios/cloud/analytics" + ] + } + ] + }, + { + "dropdown": "Flutter", + "icon": "flutter", + "description": "Wallet SDK on Flutter", + "pages": [ + "wallets/flutter/installation", + "wallets/flutter/usage", + "wallets/flutter/one-click-auth", + "wallets/flutter/mobile-linking", + "wallets/flutter/link-mode", + "wallets/flutter/verify", + "wallets/flutter/eip5792", + "wallets/flutter/chain-abstraction", + { + "group": "Cloud", + "pages": [ + "wallets/flutter/cloud/explorer-submission", + "wallets/flutter/cloud/relay", + "wallets/flutter/cloud/analytics" + ] + } + ] + }, + { + "dropdown": "React Native", + "icon": "mobile-screen-button", + "description": "Wallet SDK on React Native", + "pages": [ + { + "group": "Core", + "pages": [ + "wallets/react-native/installation", + "wallets/react-native/usage", + "wallets/react-native/one-click-auth", + "wallets/react-native/mobile-linking", + "wallets/react-native/link-mode", + "wallets/react-native/verify", + "wallets/react-native/eip5792", + "wallets/react-native/chain-abstraction", + "wallets/react-native/best-practices", + "wallets/react-native/resources" + ] + }, + { + "group": "Cloud", + "pages": [ + "wallets/react-native/cloud/explorer-submission", + "wallets/react-native/cloud/relay", + "wallets/react-native/cloud/analytics" + ] + } + ] + }, + { + "dropdown": ".NET", + "icon": "code", + "description": "Wallet SDK on .NET", + "pages": [ + "wallets/c-sharp/installation", + "wallets/c-sharp/usage", + "wallets/c-sharp/verify", + { + "group": "Cloud", + "pages": [ + "wallets/c-sharp/cloud/explorer-submission", + "wallets/c-sharp/cloud/relay", + "wallets/c-sharp/cloud/analytics" + ] + } + ] + } + ] + }, + { + "tab": "Apps", + "icon": "layer-group", + "groups": [ + { + "group": "Getting Started", + "pages": [ + "apps/overview" + ] + }, + { + "group": "Installation", + "pages": [ + "apps/react/installation", + "apps/next/installation", + "apps/vue/installation", + "apps/javascript/installation" + ] + }, + { + "group": "Guides", + "pages": [ + "wallets/guides/tonconnect-walletconnect" + ] + } + ] + }, + { + "tab": "Network", + "icon": "globe", + "groups": [ + { + "group": "Getting started", + "pages": [ + "network/index" + ] + }, + { + "group": "Participants", + "pages": [ + "network/service-nodes", + "network/wallets" + ] + }, + { + "group": "WCT Token", + "pages": [ + "network/WCT-token/overview", + "network/WCT-token/rewards", + "network/WCT-token/service-node-rewards", + "network/WCT-token/staking", + "network/WCT-token/governance", + "network/WCT-token/contracts", + "network/WCT-token/faq" + ] + }, + { + "group": "Resources", + "pages": [ + "network/specs" + ] + } + ] + }, { "tab": "Payments", "icon": "credit-card", "groups": [ { - "group": "WalletConnect Pay", + "group": "Get Started", "pages": [ "payments/overview", "payments/for-users", - "payments/token-and-chain-coverage", - "payments/fiat-coverage", - "payments/wallet-coverage", - "payments/cex-coverage", + { + "group": "Coverage & Reference", + "pages": [ + "payments/token-and-chain-coverage", + "payments/fiat-coverage", + "payments/wallet-coverage", + "payments/cex-coverage" + ] + }, "payments/test-mode", - "payments/webhooks" + "payments/webhooks", + { + "group": "Webhook Reference", + "pages": [ + "payments/webhook-event-reference", + "payments/webhook-events/payment-created", + "payments/webhook-events/payment-processing", + "payments/webhook-events/payment-succeeded", + "payments/webhook-events/payment-failed", + "payments/webhook-events/payment-expired", + "payments/webhook-events/payment-cancelled", + "payments/webhook-events/payment-settled" + ] + } ] }, { - "group": "WalletConnect Pay for Merchants", + "group": "For PSPs", "pages": [ - "payments/merchant/quickstart" + "payments/psps/overview", + { + "group": "Headless SDK", + "pages": [ + "payments/psps/headless-sdk/overview", + "payments/psps/headless-sdk/how-it-works", + "payments/psps/headless-sdk/implementation", + "payments/psps/headless-sdk/packages-reference" + ] + } ] }, { - "group": "Merchant API", + "group": "For Merchants", "pages": [ + "payments/merchant/quickstart", "payments/merchant-api/logo-specification" ] }, { - "group": "WalletConnect Pay for Wallets", + "group": "For Wallets", "pages": [ "payments/wallets/overview", { @@ -74,7 +528,7 @@ ] }, { - "group": "Integrate using the Wallet SDK", + "group": "Integrate Wallet SDK", "pages": [ "payments/wallets/walletkit/kotlin", "payments/wallets/walletkit/swift", @@ -84,6 +538,7 @@ ] }, "payments/wallets/api-first", + "payments/wallets/tap-to-pay", "payments/wallets/webview", { "group": "Token & Chain Support", @@ -92,27 +547,11 @@ "payments/wallets/token-chain-support/usdt-support", "payments/wallets/token-chain-support/solana-support" ] - }, - "payments/wallets/tap-to-pay" - ] - }, - { - "group": "WalletConnect Pay for PSPs", - "pages": [ - "payments/psps/overview", - { - "group": "Headless SDK", - "pages": [ - "payments/psps/headless-sdk/overview", - "payments/psps/headless-sdk/how-it-works", - "payments/psps/headless-sdk/implementation", - "payments/psps/headless-sdk/packages-reference" - ] } ] }, { - "group": "Ecommerce and Online Checkout", + "group": "For Checkout", "pages": [ "payments/ecommerce/overview", "payments/ecommerce/integration", @@ -120,7 +559,7 @@ ] }, { - "group": "WalletConnect AI Agent SDK", + "group": "AI Agents", "pages": [ "agents/overview" ] @@ -139,52 +578,52 @@ { "group": "Overview", "pages": [ - "api-reference/index", - "api-reference/authentication", - "api-reference/versioning" + "payments/api-reference/index", + "payments/api-reference/authentication", + "payments/api-reference/versioning" ] }, { "group": "Gateway", "pages": [ - "api-reference/2026-02-18/get-v1-gateway-payment-id", - "api-reference/2026-02-18/post-v1-gateway-payment-id-cancel", - "api-reference/2026-02-18/post-v1-gateway-payment-id-confirm", - "api-reference/2026-02-18/post-v1-gateway-payment-id-fetch", - "api-reference/2026-02-18/post-v1-gateway-payment-id-options", - "api-reference/2026-02-18/get-v1-gateway-payment-id-status" + "payments/api-reference/2026-02-18/get-v1-gateway-payment-id", + "payments/api-reference/2026-02-18/post-v1-gateway-payment-id-cancel", + "payments/api-reference/2026-02-18/post-v1-gateway-payment-id-confirm", + "payments/api-reference/2026-02-18/post-v1-gateway-payment-id-fetch", + "payments/api-reference/2026-02-18/post-v1-gateway-payment-id-options", + "payments/api-reference/2026-02-18/get-v1-gateway-payment-id-status" ] }, { "group": "Payments", "pages": [ - "api-reference/2026-02-18/post-v1-payments", - "api-reference/2026-02-18/get-v1-payments", - "api-reference/2026-02-18/get-v1-payments-id", - "api-reference/2026-02-18/post-v1-payments-id-cancel", - "api-reference/2026-02-18/get-v1-payments-id-status" + "payments/api-reference/2026-02-18/post-v1-payments", + "payments/api-reference/2026-02-18/get-v1-payments", + "payments/api-reference/2026-02-18/get-v1-payments-id", + "payments/api-reference/2026-02-18/post-v1-payments-id-cancel", + "payments/api-reference/2026-02-18/get-v1-payments-id-status" ] }, { "group": "Refunds", "pages": [ - "api-reference/2026-02-18/post-v1-refunds" + "payments/api-reference/2026-02-18/post-v1-refunds" ] }, { "group": "Merchants", "pages": [ - "api-reference/2026-02-18/get-v1-merchants-payments", - "api-reference/2026-02-18/post-v1-merchants", - "api-reference/2026-02-18/get-v1-merchants", - "api-reference/2026-02-18/get-v1-merchants-merchantid", - "api-reference/2026-02-18/patch-v1-merchants-merchantid", - "api-reference/2026-02-18/delete-v1-merchants-merchantid", - "api-reference/2026-02-18/get-v1-merchants-merchantid-settlements", - "api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto", - "api-reference/2026-02-18/put-v1-merchants-merchantid-settlements-crypto-id", - "api-reference/2026-02-18/delete-v1-merchants-merchantid-settlements-crypto-id", - "api-reference/2026-02-18/get-v1-merchants-merchant-id-payments" + "payments/api-reference/2026-02-18/get-v1-merchants-payments", + "payments/api-reference/2026-02-18/post-v1-merchants", + "payments/api-reference/2026-02-18/get-v1-merchants", + "payments/api-reference/2026-02-18/get-v1-merchants-merchantid", + "payments/api-reference/2026-02-18/patch-v1-merchants-merchantid", + "payments/api-reference/2026-02-18/delete-v1-merchants-merchantid", + "payments/api-reference/2026-02-18/get-v1-merchants-merchantid-settlements", + "payments/api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto", + "payments/api-reference/2026-02-18/put-v1-merchants-merchantid-settlements-crypto-id", + "payments/api-reference/2026-02-18/delete-v1-merchants-merchantid-settlements-crypto-id", + "payments/api-reference/2026-02-18/get-v1-merchants-merchant-id-payments" ] } ] @@ -195,78 +634,83 @@ { "group": "Overview", "pages": [ - "api-reference/index", - "api-reference/authentication", - "api-reference/versioning" + "payments/api-reference/index", + "payments/api-reference/authentication", + "payments/api-reference/versioning" ] }, { "group": "Gateway", "pages": [ - "api-reference/2026-02-19.preview/get-v1-gateway-payment-id", - "api-reference/2026-02-19.preview/post-v1-gateway-payment-id-cancel", - "api-reference/2026-02-19.preview/post-v1-gateway-payment-id-confirm", - "api-reference/2026-02-19.preview/post-v1-gateway-payment-id-fetch", - "api-reference/2026-02-19.preview/post-v1-gateway-payment-id-options", - "api-reference/2026-02-19.preview/get-v1-gateway-payment-id-status" + "payments/api-reference/2026-02-19.preview/get-v1-gateway-payment-id", + "payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-cancel", + "payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-confirm", + "payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-fetch", + "payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-options", + "payments/api-reference/2026-02-19.preview/get-v1-gateway-payment-id-status" ] }, { "group": "Payments", "pages": [ - "api-reference/2026-02-19.preview/post-v1-payments", - "api-reference/2026-02-19.preview/get-v1-payments", - "api-reference/2026-02-19.preview/get-v1-payments-id", - "api-reference/2026-02-19.preview/post-v1-payments-id-cancel", - "api-reference/2026-02-19.preview/get-v1-payments-id-status" + "payments/api-reference/2026-02-19.preview/post-v1-payments", + "payments/api-reference/2026-02-19.preview/get-v1-payments", + "payments/api-reference/2026-02-19.preview/get-v1-payments-id", + "payments/api-reference/2026-02-19.preview/post-v1-payments-id-cancel", + "payments/api-reference/2026-02-19.preview/get-v1-payments-id-status" ] }, { "group": "Refunds", "pages": [ - "api-reference/2026-02-19.preview/post-v1-refunds" + "payments/api-reference/2026-02-19.preview/post-v1-refunds" ] }, { "group": "Merchants", "pages": [ - "api-reference/2026-02-19.preview/get-v1-merchants-payments", - "api-reference/2026-02-19.preview/post-v1-merchants", - "api-reference/2026-02-19.preview/get-v1-merchants", - "api-reference/2026-02-19.preview/get-v1-merchants-merchantid", - "api-reference/2026-02-19.preview/patch-v1-merchants-merchantid", - "api-reference/2026-02-19.preview/delete-v1-merchants-merchantid", - "api-reference/2026-02-19.preview/get-v1-merchants-merchantid-settlements", - "api-reference/2026-02-19.preview/post-v1-merchants-merchantid-settlements-crypto", - "api-reference/2026-02-19.preview/put-v1-merchants-merchantid-settlements-crypto-id", - "api-reference/2026-02-19.preview/delete-v1-merchants-merchantid-settlements-crypto-id", - "api-reference/2026-02-19.preview/get-v1-merchants-merchant-id-payments" + "payments/api-reference/2026-02-19.preview/get-v1-merchants-payments", + "payments/api-reference/2026-02-19.preview/post-v1-merchants", + "payments/api-reference/2026-02-19.preview/get-v1-merchants", + "payments/api-reference/2026-02-19.preview/get-v1-merchants-merchantid", + "payments/api-reference/2026-02-19.preview/patch-v1-merchants-merchantid", + "payments/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid", + "payments/api-reference/2026-02-19.preview/get-v1-merchants-merchantid-settlements", + "payments/api-reference/2026-02-19.preview/post-v1-merchants-merchantid-settlements-crypto", + "payments/api-reference/2026-02-19.preview/put-v1-merchants-merchantid-settlements-crypto-id", + "payments/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid-settlements-crypto-id", + "payments/api-reference/2026-02-19.preview/get-v1-merchants-merchant-id-payments" ] } ] } ] - }, - { - "tab": "Webhook Reference", - "icon": "webhook", - "groups": [ - { - "group": "Payment events", - "pages": [ - "payments/webhook-event-reference", - "payments/webhook-events/payment-created", - "payments/webhook-events/payment-processing", - "payments/webhook-events/payment-succeeded", - "payments/webhook-events/payment-failed", - "payments/webhook-events/payment-expired", - "payments/webhook-events/payment-cancelled", - "payments/webhook-events/payment-settled" - ] - } - ] } - ] + ], + "global": { + "anchors": [ + { + "anchor": "WalletConnect", + "href": "https://walletconnect.com", + "icon": "globe" + }, + { + "anchor": "Blog", + "href": "https://walletconnect.com/blog", + "icon": "newspaper" + }, + { + "anchor": "Whitepaper", + "href": "https://whitepaper.walletconnect.network/", + "icon": "book" + }, + { + "anchor": "Report an Issue", + "href": "/report-an-issue", + "icon": "bug" + } + ] + } }, "logo": { "light": "/logo/light.svg", diff --git a/images/cloud/11.png b/images/cloud/11.png new file mode 100644 index 0000000..98ba286 Binary files /dev/null and b/images/cloud/11.png differ diff --git a/images/cloud/6.png b/images/cloud/6.png new file mode 100644 index 0000000..982cbf1 Binary files /dev/null and b/images/cloud/6.png differ diff --git a/images/cloud/7.png b/images/cloud/7.png new file mode 100644 index 0000000..e5ce05e Binary files /dev/null and b/images/cloud/7.png differ diff --git a/images/cloud/8.png b/images/cloud/8.png new file mode 100644 index 0000000..2f910f6 Binary files /dev/null and b/images/cloud/8.png differ diff --git a/images/cloud/9.png b/images/cloud/9.png new file mode 100644 index 0000000..0452015 Binary files /dev/null and b/images/cloud/9.png differ diff --git a/images/help-desk-bubble.avif b/images/help-desk-bubble.avif new file mode 100644 index 0000000..2e66ab0 Binary files /dev/null and b/images/help-desk-bubble.avif differ diff --git a/index.mdx b/index.mdx index bb687a5..20b315f 100644 --- a/index.mdx +++ b/index.mdx @@ -1,197 +1,100 @@ --- mode: "frame" -title: "WalletConnect Pay Documentation" +title: "WalletConnect Documentation" sidebarTitle: "Home" -description: "Documentation for WalletConnect Pay. Integrate secure crypto payments into your app with step-by-step guides, APIs, and best practices." +description: "The Connectivity Layer for the Financial Internet" --- -import { Container, Title} from '/snippets/home/utility.mdx'; -import { ProductTiles } from '/snippets/home/product-tiles.mdx'; -import { appCategories } from '/snippets/home/data.mdx'; +import { Container, Title } from '/snippets/home/utility.mdx'; import { ProductCard } from '/snippets/home/product-cards.mdx';
-
- WalletConnect Pay +
+ WalletConnect
-

- The easiest way to enable crypto payments from any wallet, any asset, anywhere. +

+ The Connectivity Layer for the Financial Internet

-
- - + +
-
- - Introducing WalletConnect Pay - -
-

- **[WalletConnect Pay](/payments/overview)** is a complete, end-to-end crypto payment solution that allows PSPs and merchants to enable crypto payments from any wallet and any asset through a single, familiar integration. + + WalletConnect Ecosystem Quickstart + +

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

+

+ Check out our quickstart guides to get started!

-
- - - - - -
-
- - - + - + type="Wallet SDK" + /> + + + -
-
    +
      - Learn more about WalletConnect Pay + Resources -

      - A curated list of educational resources about WalletConnect Pay and crypto payments. -

      -
      - A clear guide to how Stablecoin and Wallet payments work. - - - Learn what it takes to make crypto payments usable in the real world. - - - Learn how to accept USDC anywhere with WalletConnect Pay. + Read the latest news and updates from the WalletConnect team - A legal deep dive into why stablecoins are not yet mainstream. + Discord community for WalletConnect Network and WCT Token - Learn what’s hindering adoption today, what’s changing globally, and the infra behind crypto payments. + Follow WalletConnect on X (Twitter) to stay updated on the announcements
      - - - -
      - - Resources - - -
      - - - Read the latest news and updates from the WalletConnect team - - - Follow WalletConnect on X (Twitter) to stay updated on the announcements - - -
diff --git a/network/WCT-token/contracts.mdx b/network/WCT-token/contracts.mdx new file mode 100644 index 0000000..1b88dc6 --- /dev/null +++ b/network/WCT-token/contracts.mdx @@ -0,0 +1,44 @@ +--- +title: "WCT Smart Contracts" +sidebarTitle: Deployed 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) \ No newline at end of file diff --git a/network/WCT-token/faq.mdx b/network/WCT-token/faq.mdx new file mode 100644 index 0000000..efe56d9 --- /dev/null +++ b/network/WCT-token/faq.mdx @@ -0,0 +1,121 @@ +--- +title: 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** \ No newline at end of file diff --git a/network/WCT-token/governance.mdx b/network/WCT-token/governance.mdx new file mode 100644 index 0000000..ae7606c --- /dev/null +++ b/network/WCT-token/governance.mdx @@ -0,0 +1,48 @@ +--- +title: Governance +sidebarTitle: Overview +--- + +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. + \ No newline at end of file diff --git a/network/WCT-token/overview.mdx b/network/WCT-token/overview.mdx new file mode 100644 index 0000000..9b8fd99 --- /dev/null +++ b/network/WCT-token/overview.mdx @@ -0,0 +1,64 @@ +--- +title: WCT Token +sidebarTitle: "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. + diff --git a/network/WCT-token/rewards.mdx b/network/WCT-token/rewards.mdx new file mode 100644 index 0000000..ab38678 --- /dev/null +++ b/network/WCT-token/rewards.mdx @@ -0,0 +1,48 @@ +--- +title: 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) \ No newline at end of file diff --git a/network/WCT-token/service-node-rewards.mdx b/network/WCT-token/service-node-rewards.mdx new file mode 100644 index 0000000..3c80fe3 --- /dev/null +++ b/network/WCT-token/service-node-rewards.mdx @@ -0,0 +1,39 @@ +--- +title: 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. + \ No newline at end of file diff --git a/network/WCT-token/staking.mdx b/network/WCT-token/staking.mdx new file mode 100644 index 0000000..75bc23f --- /dev/null +++ b/network/WCT-token/staking.mdx @@ -0,0 +1,188 @@ +--- +title: "WCT Staking" +--- + +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. \ No newline at end of file diff --git a/network/index.mdx b/network/index.mdx new file mode 100644 index 0000000..a9241ff --- /dev/null +++ b/network/index.mdx @@ -0,0 +1,62 @@ +--- +title: "Network" +--- + +## Overview + +The WalletConnect Network is a network of apps, wallets and chains, facilitating end-users to connect to apps using any supported wallet on any supported chain. 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 80,000+ applications. + +Since its inception in 2018, the Network's design emphasizes interoperability and composability to efficiently enable interoperability between apps, wallets, fintechs, chains and TradFi. + +## Technology + +The WalletConnect Network's key components include: + +1. **Service Nodes**; database nodes that form the backbone of the network's storage layer. They operate on a consistent-hashing based distributed database. + +2. **Gateway Nodes**; responsible for facilitating encrypted communications and data routing between wallets and applications. + +3. **Relay Service**; connects users' wallets to dapps. + +## Permissioned Network + +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 centralized and managed by WalletConnect. + +This allows WalletConnect to provide a secure and reliable service for enterprise-grade apps and wallets. + +## Network Participants + +WalletConnect Network Architecture + +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. + +## Learn more about the WalletConnect Network + + + + Learn about the WalletConnect network's governance model. + + + Learn how WCT powers the network's governance and incentivizes participants. + + + Read more about the network's design, architecture, and core principles. + + diff --git a/network/service-nodes.mdx b/network/service-nodes.mdx new file mode 100644 index 0000000..76fbc7b --- /dev/null +++ b/network/service-nodes.mdx @@ -0,0 +1,82 @@ +--- +title: 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. \ No newline at end of file diff --git a/network/specs.mdx b/network/specs.mdx new file mode 100644 index 0000000..377c259 --- /dev/null +++ b/network/specs.mdx @@ -0,0 +1,10 @@ +--- +title: "WalletConnect Specs" +sidebarTitle: "Specs" +--- + +The WalletConnect Specs document the core WalletConnect protocol — how it's built underneath, at a level of technical detail beyond what's covered in this documentation. + + + Read the protocol specification. + diff --git a/network/wallets.mdx b/network/wallets.mdx new file mode 100644 index 0000000..ee22f18 --- /dev/null +++ b/network/wallets.mdx @@ -0,0 +1,15 @@ +--- +title: "Wallets" +sidebarTitle: "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](/network/WCT-token/rewards) documentation.​ diff --git a/api-reference/2026-02-18/delete-v1-merchants-merchantid-settlements-crypto-id.mdx b/payments/api-reference/2026-02-18/delete-v1-merchants-merchantid-settlements-crypto-id.mdx similarity index 100% rename from api-reference/2026-02-18/delete-v1-merchants-merchantid-settlements-crypto-id.mdx rename to payments/api-reference/2026-02-18/delete-v1-merchants-merchantid-settlements-crypto-id.mdx diff --git a/api-reference/2026-02-18/delete-v1-merchants-merchantid.mdx b/payments/api-reference/2026-02-18/delete-v1-merchants-merchantid.mdx similarity index 100% rename from api-reference/2026-02-18/delete-v1-merchants-merchantid.mdx rename to payments/api-reference/2026-02-18/delete-v1-merchants-merchantid.mdx diff --git a/api-reference/2026-02-18/get-v1-gateway-payment-id-status.mdx b/payments/api-reference/2026-02-18/get-v1-gateway-payment-id-status.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-gateway-payment-id-status.mdx rename to payments/api-reference/2026-02-18/get-v1-gateway-payment-id-status.mdx diff --git a/api-reference/2026-02-18/get-v1-gateway-payment-id.mdx b/payments/api-reference/2026-02-18/get-v1-gateway-payment-id.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-gateway-payment-id.mdx rename to payments/api-reference/2026-02-18/get-v1-gateway-payment-id.mdx diff --git a/api-reference/2026-02-18/get-v1-merchants-merchant-id-payments.mdx b/payments/api-reference/2026-02-18/get-v1-merchants-merchant-id-payments.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-merchants-merchant-id-payments.mdx rename to payments/api-reference/2026-02-18/get-v1-merchants-merchant-id-payments.mdx diff --git a/api-reference/2026-02-18/get-v1-merchants-merchantid-settlements.mdx b/payments/api-reference/2026-02-18/get-v1-merchants-merchantid-settlements.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-merchants-merchantid-settlements.mdx rename to payments/api-reference/2026-02-18/get-v1-merchants-merchantid-settlements.mdx diff --git a/api-reference/2026-02-18/get-v1-merchants-merchantid.mdx b/payments/api-reference/2026-02-18/get-v1-merchants-merchantid.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-merchants-merchantid.mdx rename to payments/api-reference/2026-02-18/get-v1-merchants-merchantid.mdx diff --git a/api-reference/2026-02-18/get-v1-merchants-payments.mdx b/payments/api-reference/2026-02-18/get-v1-merchants-payments.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-merchants-payments.mdx rename to payments/api-reference/2026-02-18/get-v1-merchants-payments.mdx diff --git a/api-reference/2026-02-18/get-v1-merchants.mdx b/payments/api-reference/2026-02-18/get-v1-merchants.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-merchants.mdx rename to payments/api-reference/2026-02-18/get-v1-merchants.mdx diff --git a/api-reference/2026-02-18/get-v1-payments-id-status.mdx b/payments/api-reference/2026-02-18/get-v1-payments-id-status.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-payments-id-status.mdx rename to payments/api-reference/2026-02-18/get-v1-payments-id-status.mdx diff --git a/api-reference/2026-02-18/get-v1-payments-id.mdx b/payments/api-reference/2026-02-18/get-v1-payments-id.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-payments-id.mdx rename to payments/api-reference/2026-02-18/get-v1-payments-id.mdx diff --git a/api-reference/2026-02-18/get-v1-payments.mdx b/payments/api-reference/2026-02-18/get-v1-payments.mdx similarity index 100% rename from api-reference/2026-02-18/get-v1-payments.mdx rename to payments/api-reference/2026-02-18/get-v1-payments.mdx diff --git a/api-reference/2026-02-18/patch-v1-merchants-merchantid.mdx b/payments/api-reference/2026-02-18/patch-v1-merchants-merchantid.mdx similarity index 100% rename from api-reference/2026-02-18/patch-v1-merchants-merchantid.mdx rename to payments/api-reference/2026-02-18/patch-v1-merchants-merchantid.mdx diff --git a/api-reference/2026-02-18/post-v1-gateway-payment-id-cancel.mdx b/payments/api-reference/2026-02-18/post-v1-gateway-payment-id-cancel.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-gateway-payment-id-cancel.mdx rename to payments/api-reference/2026-02-18/post-v1-gateway-payment-id-cancel.mdx diff --git a/api-reference/2026-02-18/post-v1-gateway-payment-id-confirm.mdx b/payments/api-reference/2026-02-18/post-v1-gateway-payment-id-confirm.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-gateway-payment-id-confirm.mdx rename to payments/api-reference/2026-02-18/post-v1-gateway-payment-id-confirm.mdx diff --git a/api-reference/2026-02-18/post-v1-gateway-payment-id-fetch.mdx b/payments/api-reference/2026-02-18/post-v1-gateway-payment-id-fetch.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-gateway-payment-id-fetch.mdx rename to payments/api-reference/2026-02-18/post-v1-gateway-payment-id-fetch.mdx diff --git a/api-reference/2026-02-18/post-v1-gateway-payment-id-options.mdx b/payments/api-reference/2026-02-18/post-v1-gateway-payment-id-options.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-gateway-payment-id-options.mdx rename to payments/api-reference/2026-02-18/post-v1-gateway-payment-id-options.mdx diff --git a/api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto.mdx b/payments/api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto.mdx rename to payments/api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto.mdx diff --git a/api-reference/2026-02-18/post-v1-merchants.mdx b/payments/api-reference/2026-02-18/post-v1-merchants.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-merchants.mdx rename to payments/api-reference/2026-02-18/post-v1-merchants.mdx diff --git a/api-reference/2026-02-18/post-v1-payments-id-cancel.mdx b/payments/api-reference/2026-02-18/post-v1-payments-id-cancel.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-payments-id-cancel.mdx rename to payments/api-reference/2026-02-18/post-v1-payments-id-cancel.mdx diff --git a/api-reference/2026-02-18/post-v1-payments.mdx b/payments/api-reference/2026-02-18/post-v1-payments.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-payments.mdx rename to payments/api-reference/2026-02-18/post-v1-payments.mdx diff --git a/api-reference/2026-02-18/post-v1-refunds.mdx b/payments/api-reference/2026-02-18/post-v1-refunds.mdx similarity index 100% rename from api-reference/2026-02-18/post-v1-refunds.mdx rename to payments/api-reference/2026-02-18/post-v1-refunds.mdx diff --git a/api-reference/2026-02-18/put-v1-merchants-merchantid-settlements-crypto-id.mdx b/payments/api-reference/2026-02-18/put-v1-merchants-merchantid-settlements-crypto-id.mdx similarity index 100% rename from api-reference/2026-02-18/put-v1-merchants-merchantid-settlements-crypto-id.mdx rename to payments/api-reference/2026-02-18/put-v1-merchants-merchantid-settlements-crypto-id.mdx diff --git a/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid-settlements-crypto-id.mdx b/payments/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid-settlements-crypto-id.mdx similarity index 100% rename from api-reference/2026-02-19.preview/delete-v1-merchants-merchantid-settlements-crypto-id.mdx rename to payments/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid-settlements-crypto-id.mdx diff --git a/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid.mdx b/payments/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid.mdx similarity index 100% rename from api-reference/2026-02-19.preview/delete-v1-merchants-merchantid.mdx rename to payments/api-reference/2026-02-19.preview/delete-v1-merchants-merchantid.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-gateway-payment-id-status.mdx b/payments/api-reference/2026-02-19.preview/get-v1-gateway-payment-id-status.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-gateway-payment-id-status.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-gateway-payment-id-status.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-gateway-payment-id.mdx b/payments/api-reference/2026-02-19.preview/get-v1-gateway-payment-id.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-gateway-payment-id.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-gateway-payment-id.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-merchants-merchant-id-payments.mdx b/payments/api-reference/2026-02-19.preview/get-v1-merchants-merchant-id-payments.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-merchants-merchant-id-payments.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-merchants-merchant-id-payments.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-merchants-merchantid-settlements.mdx b/payments/api-reference/2026-02-19.preview/get-v1-merchants-merchantid-settlements.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-merchants-merchantid-settlements.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-merchants-merchantid-settlements.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-merchants-merchantid.mdx b/payments/api-reference/2026-02-19.preview/get-v1-merchants-merchantid.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-merchants-merchantid.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-merchants-merchantid.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-merchants-payments.mdx b/payments/api-reference/2026-02-19.preview/get-v1-merchants-payments.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-merchants-payments.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-merchants-payments.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-merchants.mdx b/payments/api-reference/2026-02-19.preview/get-v1-merchants.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-merchants.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-merchants.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-payments-id-status.mdx b/payments/api-reference/2026-02-19.preview/get-v1-payments-id-status.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-payments-id-status.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-payments-id-status.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-payments-id.mdx b/payments/api-reference/2026-02-19.preview/get-v1-payments-id.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-payments-id.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-payments-id.mdx diff --git a/api-reference/2026-02-19.preview/get-v1-payments.mdx b/payments/api-reference/2026-02-19.preview/get-v1-payments.mdx similarity index 100% rename from api-reference/2026-02-19.preview/get-v1-payments.mdx rename to payments/api-reference/2026-02-19.preview/get-v1-payments.mdx diff --git a/api-reference/2026-02-19.preview/patch-v1-merchants-merchantid.mdx b/payments/api-reference/2026-02-19.preview/patch-v1-merchants-merchantid.mdx similarity index 100% rename from api-reference/2026-02-19.preview/patch-v1-merchants-merchantid.mdx rename to payments/api-reference/2026-02-19.preview/patch-v1-merchants-merchantid.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-cancel.mdx b/payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-cancel.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-gateway-payment-id-cancel.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-cancel.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-confirm.mdx b/payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-confirm.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-gateway-payment-id-confirm.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-confirm.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-fetch.mdx b/payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-fetch.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-gateway-payment-id-fetch.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-fetch.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-options.mdx b/payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-options.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-gateway-payment-id-options.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-gateway-payment-id-options.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-merchants-merchantid-settlements-crypto.mdx b/payments/api-reference/2026-02-19.preview/post-v1-merchants-merchantid-settlements-crypto.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-merchants-merchantid-settlements-crypto.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-merchants-merchantid-settlements-crypto.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-merchants.mdx b/payments/api-reference/2026-02-19.preview/post-v1-merchants.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-merchants.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-merchants.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-payments-id-cancel.mdx b/payments/api-reference/2026-02-19.preview/post-v1-payments-id-cancel.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-payments-id-cancel.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-payments-id-cancel.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-payments.mdx b/payments/api-reference/2026-02-19.preview/post-v1-payments.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-payments.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-payments.mdx diff --git a/api-reference/2026-02-19.preview/post-v1-refunds.mdx b/payments/api-reference/2026-02-19.preview/post-v1-refunds.mdx similarity index 100% rename from api-reference/2026-02-19.preview/post-v1-refunds.mdx rename to payments/api-reference/2026-02-19.preview/post-v1-refunds.mdx diff --git a/api-reference/2026-02-19.preview/put-v1-merchants-merchantid-settlements-crypto-id.mdx b/payments/api-reference/2026-02-19.preview/put-v1-merchants-merchantid-settlements-crypto-id.mdx similarity index 100% rename from api-reference/2026-02-19.preview/put-v1-merchants-merchantid-settlements-crypto-id.mdx rename to payments/api-reference/2026-02-19.preview/put-v1-merchants-merchantid-settlements-crypto-id.mdx diff --git a/api-reference/authentication.mdx b/payments/api-reference/authentication.mdx similarity index 100% rename from api-reference/authentication.mdx rename to payments/api-reference/authentication.mdx diff --git a/api-reference/index.mdx b/payments/api-reference/index.mdx similarity index 100% rename from api-reference/index.mdx rename to payments/api-reference/index.mdx diff --git a/api-reference/versioning.mdx b/payments/api-reference/versioning.mdx similarity index 100% rename from api-reference/versioning.mdx rename to payments/api-reference/versioning.mdx diff --git a/payments/index.mdx b/payments/index.mdx new file mode 100644 index 0000000..e793b60 --- /dev/null +++ b/payments/index.mdx @@ -0,0 +1,121 @@ +--- +mode: "frame" +title: "WalletConnect Pay Documentation" +sidebarTitle: "Home" +description: "Documentation for WalletConnect Pay. Integrate secure crypto payments into your app with step-by-step guides, APIs, and best practices." +--- + +import { Container, Title } from '/snippets/home/utility.mdx'; +import { ProductCard } from '/snippets/home/product-cards.mdx'; + +
+ +
+
+
+ WalletConnect Pay +
+

+ WalletConnect Pay is a complete, end-to-end stablecoin and crypto payment solution that allows PSPs and merchants to enable crypto payments from any wallet and any asset through a single, familiar integration. +

+
+
+
+ + +
+

+ WalletConnect Pay serves Merchants, PSPs, Wallets and eCommerce. Choose your integration method below to quickstart. +

+
+ + + + + + +
    + + + +
    + + How does WalletConnect Pay work? + +
    + +
    + + +
    + + Resources + +

    + Learn more about WalletConnect Pay and the wider WalletConnect brand. +

    +
    + + + Dive into the API reference to explore more + + + Read the latest news and updates from the WalletConnect team + + +
    +
diff --git a/payments/merchant-api/logo-specification.mdx b/payments/merchant-api/logo-specification.mdx index 6a7704d..f9fde54 100644 --- a/payments/merchant-api/logo-specification.mdx +++ b/payments/merchant-api/logo-specification.mdx @@ -6,7 +6,7 @@ sidebarTitle: "Logo specification" The `iconUrl` field on the Merchant API accepts a public HTTPS URL pointing to a merchant logo. The logo renders at the moment of payment, so dimensions and format are a direct trust signal. A stretched or pixelated logo undermines buyer confidence at the exact step where confidence matters most. -This guide covers the recommended specification for the `iconUrl` field used by [`POST /v1/merchants`](/api-reference/2026-02-18/post-v1-merchants) (create) and [`PATCH /v1/merchants/{merchantId}`](/api-reference/2026-02-18/patch-v1-merchants-merchantid) (update). +This guide covers the recommended specification for the `iconUrl` field used by [`POST /v1/merchants`](/payments/api-reference/2026-02-18/post-v1-merchants) (create) and [`PATCH /v1/merchants/{merchantId}`](/payments/api-reference/2026-02-18/patch-v1-merchants-merchantid) (update). ## Where the logo appears diff --git a/payments/merchant/quickstart.mdx b/payments/merchant/quickstart.mdx index 2a90b63..4a1345c 100644 --- a/payments/merchant/quickstart.mdx +++ b/payments/merchant/quickstart.mdx @@ -50,7 +50,7 @@ curl "$WCP_BASE/v1/merchants" \ A `200 OK` with a (possibly empty) `data` array confirms the key works. A `401` means the key is wrong; a `403` means the key is valid but lacks permission for this account. -Pin requests to a specific API version with the `WCP-Version` header (for example, `WCP-Version: 2026-02-18`). Without it, your account's default version is used. See [Versioning](/api-reference/versioning) for the full policy. +Pin requests to a specific API version with the `WCP-Version` header (for example, `WCP-Version: 2026-02-18`). Without it, your account's default version is used. See [Versioning](/payments/api-reference/versioning) for the full policy. @@ -92,7 +92,7 @@ export WCP_MERCHANT_ID="mrch_7kBz2qR9xPvLmN4Yw" `Idempotency-Key` is required on this endpoint. Use a fresh UUID per merchant — replays with the same key return the original result instead of creating a duplicate. -See the full schema and field reference in [Create a merchant](/api-reference/latest/post-v1-merchants). +See the full schema and field reference in [Create a merchant](/payments/api-reference/latest/post-v1-merchants). @@ -118,10 +118,10 @@ curl -X POST "$WCP_BASE/v1/merchants/$WCP_MERCHANT_ID/settlements/crypto" \ The example above registers USDC on Base. For the full list of supported `asset` values and the chains we settle to, see [Token & Chain Coverage](/payments/token-and-chain-coverage). -Each `(merchant, asset)` pair must be unique — registering the same asset twice returns `settlement_asset_conflict`. To change a destination, use [Update a crypto settlement](/api-reference/latest/put-v1-merchants-merchantid-settlements-crypto-id) instead. +Each `(merchant, asset)` pair must be unique — registering the same asset twice returns `settlement_asset_conflict`. To change a destination, use [Update a crypto settlement](/payments/api-reference/latest/put-v1-merchants-merchantid-settlements-crypto-id) instead. -See the full schema in [Create crypto settlements](/api-reference/latest/post-v1-merchants-merchantid-settlements-crypto). +See the full schema in [Create crypto settlements](/payments/api-reference/latest/post-v1-merchants-merchantid-settlements-crypto). @@ -182,7 +182,7 @@ Use `isFinal` to decide when to stop polling. While the payment is still in flig In test mode the `txId` is synthetic (`test:{paymentId}`) — no transaction is executed on-chain. Live payments return a real transaction hash. -See the full response schema in [Get the payment status](/api-reference/latest/get-v1-payments-id-status). +See the full response schema in [Get the payment status](/payments/api-reference/latest/get-v1-payments-id-status). diff --git a/payments/psps/headless-sdk/implementation.mdx b/payments/psps/headless-sdk/implementation.mdx index d660784..979aeea 100644 --- a/payments/psps/headless-sdk/implementation.mdx +++ b/payments/psps/headless-sdk/implementation.mdx @@ -369,7 +369,7 @@ WCP_WALLET_API_KEY= The full public API of pay-core, pay-state, pay-react, and pay-appkit. - + The Gateway and Payments endpoints behind the SDK. diff --git a/payments/psps/headless-sdk/packages-reference.mdx b/payments/psps/headless-sdk/packages-reference.mdx index 43a4f98..2a99be2 100644 --- a/payments/psps/headless-sdk/packages-reference.mdx +++ b/payments/psps/headless-sdk/packages-reference.mdx @@ -280,7 +280,7 @@ function useAppKitWalletProvider(appKit: AppKit | undefined, options?: UseAppKit The step-by-step React / Next.js walkthrough. - + The Gateway and Payments endpoints behind the SDK. diff --git a/payments/psps/overview.mdx b/payments/psps/overview.mdx index 7a393eb..95a938e 100644 --- a/payments/psps/overview.mdx +++ b/payments/psps/overview.mdx @@ -41,7 +41,7 @@ Reach for the **Headless SDK** when you need to **own the experience** end to en Own the full checkout experience with the `@walletconnect/pay-*` packages. - + The Gateway and Payments endpoints behind WalletConnect Pay. diff --git a/payments/test-mode.mdx b/payments/test-mode.mdx index e7f5388..c8d0d5a 100644 --- a/payments/test-mode.mdx +++ b/payments/test-mode.mdx @@ -133,7 +133,7 @@ Switching from test to live is just a key swap: create a **live key** (prefix `w Create your first test payment in six steps. - + How API keys and the Api-Key header work. diff --git a/payments/token-and-chain-coverage.mdx b/payments/token-and-chain-coverage.mdx index 5f46b5a..bf65147 100644 --- a/payments/token-and-chain-coverage.mdx +++ b/payments/token-and-chain-coverage.mdx @@ -14,7 +14,7 @@ This list will be updated over time as assets move from beta into General Availa ## Supported tokens and chains -When creating crypto settlements, use the CAIP-19 token identifier below when specifying the settlement token contract. See the [Create crypto settlements](/api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto) endpoint for details. +When creating crypto settlements, use the CAIP-19 token identifier below when specifying the settlement token contract. See the [Create crypto settlements](/payments/api-reference/2026-02-18/post-v1-merchants-merchantid-settlements-crypto) endpoint for details. | Token | Chain | CAIP-19 | Acceptance | Settlement | diff --git a/payments/wallets/api-first.mdx b/payments/wallets/api-first.mdx index 896895f..fdda511 100644 --- a/payments/wallets/api-first.mdx +++ b/payments/wallets/api-first.mdx @@ -225,4 +225,4 @@ If a payment or route expires mid-flow, the API returns a `410` (payment expired ## API Reference -For request/response schemas and examples for each Gateway endpoint, see the **[API Reference](/api-reference)**. +For request/response schemas and examples for each Gateway endpoint, see the **[API Reference](/payments/api-reference)**. diff --git a/payments/wallets/webview.mdx b/payments/wallets/webview.mdx index da42d04..e72c87d 100644 --- a/payments/wallets/webview.mdx +++ b/payments/wallets/webview.mdx @@ -553,7 +553,7 @@ const res = await fetch( const { status } = await res.json(); // "succeeded" | "processing" | "failed" | … ``` -See the [Payments Status API](/api-reference/2026-02-18/get-v1-payments-id-status) for the full response schema. +See the [Payments Status API](/payments/api-reference/2026-02-18/get-v1-payments-id-status) for the full response schema. ## Best Practices diff --git a/payments/webhooks.mdx b/payments/webhooks.mdx index 6355ca1..0c24db5 100644 --- a/payments/webhooks.mdx +++ b/payments/webhooks.mdx @@ -8,7 +8,7 @@ Webhooks push payment lifecycle events to an HTTPS endpoint you control, as they ## Webhooks vs. polling -Polling [`GET /v1/payments/{id}/status`](/api-reference/latest/get-v1-payments-id-status) is the primary way to validate payment status during the purchase cycle: create the payment, then poll until the status is final. Webhooks come on top as an enhancement. They usually tell you first, and with far fewer requests, so you can update the order status in your UI, notify a customer, or kick off downstream processing the moment something happens. +Polling [`GET /v1/payments/{id}/status`](/payments/api-reference/latest/get-v1-payments-id-status) is the primary way to validate payment status during the purchase cycle: create the payment, then poll until the status is final. Webhooks come on top as an enhancement. They usually tell you first, and with far fewer requests, so you can update the order status in your UI, notify a customer, or kick off downstream processing the moment something happens. When a webhook event arrives, its payload already contains everything you need. Every event is a full signed snapshot of the payment and carries a monotonic `payment_state_version`, so verifying the signature, deduplicating by `id`, and applying the version guard is enough; there is no need to call the API from inside your handler. @@ -173,7 +173,7 @@ That's roughly **28 hours** of automatic retries. After the final attempt the de Replay operations reach back 14 days at most. And if every delivery to an endpoint keeps failing for **5 days**, the endpoint is **disabled** and stops receiving events. -Because a misconfigured URL or a disabled endpoint drops deliveries silently, don't rely on noticing an outage. Run a **periodic reconciliation job**: poll [`GET /v1/payments/{id}/status`](/api-reference/latest/get-v1-payments-id-status) for any payment your records still show in a non-final state after its `expires_at` has passed, and [`GET /v1/payments`](/api-reference/latest/get-v1-payments), which returns `settled`, for succeeded payments still awaiting settlement. Use the endpoint's delivery history in the dashboard to debug what went wrong. +Because a misconfigured URL or a disabled endpoint drops deliveries silently, don't rely on noticing an outage. Run a **periodic reconciliation job**: poll [`GET /v1/payments/{id}/status`](/payments/api-reference/latest/get-v1-payments-id-status) for any payment your records still show in a non-final state after its `expires_at` has passed, and [`GET /v1/payments`](/payments/api-reference/latest/get-v1-payments), which returns `settled`, for succeeded payments still awaiting settlement. Use the endpoint's delivery history in the dashboard to debug what went wrong. To stop deliveries entirely (for example, when decommissioning an integration), delete the endpoint from the dashboard's **Webhooks** page. Deletion takes effect immediately and cannot be undone. @@ -549,7 +549,7 @@ Webhook configuration does **not** carry over from test to live; they are separa Simulate every payment status transition and watch the webhooks arrive. - + The polling endpoint to confirm state on the critical path. diff --git a/report-an-issue.mdx b/report-an-issue.mdx new file mode 100644 index 0000000..6978cbd --- /dev/null +++ b/report-an-issue.mdx @@ -0,0 +1,35 @@ +--- +title: Report an Issue +description: Report bugs, share feedback, and get help through the WalletConnect Dashboard +--- + +The WalletConnect Dashboard is your single place to report bugs and share feedback. + +## How to Report an Issue + +Follow these steps to get in touch with our support team: + + + + Go to [dashboard.walletconnect.com](https://dashboard.walletconnect.com) and log in with your account. + + + Once logged in, click the chat bubble in the bottom-right corner of the page, or choose **Report a bug** from the Help Center sidebar. + + + + + + + From the help desk modal, select: + + - **File a bug report** - Submit the issue to our support team. + - **Check our docs** - Browse through our documentation for answers. + + + +Our team will review your submission and get back to you as soon as possible. + +## Support Response Times + +We don't offer guaranteed response times. Our team answers general issues as capacity allows, so replies may take anywhere from a few hours to several days. Thanks for your patience. diff --git a/scripts/generate-specs.mjs b/scripts/generate-specs.mjs index c5a6481..bbd87d8 100644 --- a/scripts/generate-specs.mjs +++ b/scripts/generate-specs.mjs @@ -86,7 +86,7 @@ function slugify(method, path) { } async function generatePages(version, spec) { - const dir = join(ROOT, "api-reference", version); + const dir = join(ROOT, "payments", "api-reference", version); await mkdir(dir, { recursive: true }); // Group by second path segment: /v1/{segment}/... @@ -112,7 +112,7 @@ async function generatePages(version, spec) { const title = (op.summary || `${method} ${path}`).replace(/["\\]/g, "\\$&"); const mdx = `---\ntitle: "${title}"\nopenapi: "api/${version}.json ${method} ${path}"\n---\n`; writes.push(writeFile(join(dir, filename), mdx)); - pages.push(`api-reference/${version}/${slug}`); + pages.push(`payments/api-reference/${version}/${slug}`); } groups.push({ group, pages }); } @@ -143,25 +143,12 @@ async function generateSpec(version) { return { version, spec: merged }; } -function findPaymentsTab(navigation) { - if (navigation.versions) { - const tabs = navigation.versions[0]?.tabs || []; - return tabs.find((t) => t.tab === "Payments"); - } - if (navigation.tabs) { - return navigation.tabs.find((t) => t.tab === "Payments"); - } - return null; -} - async function buildNavigation( currentConfig, stableVersions, previewVersions, versionSpecs ) { - const paymentsTab = findPaymentsTab(currentConfig.navigation); - const makeVersionEntry = async (version, { tag, isDefault } = {}) => { const spec = versionSpecs.get(version); const groups = await generatePages(version, spec); @@ -171,16 +158,9 @@ async function buildNavigation( const entry = { version, - tabs: [ - paymentsTab, - { - tab: "API Reference", - icon: "code", - groups: [ - { group: "Overview", pages: ["api-reference/index", "api-reference/versioning"] }, - ...groups, - ], - }, + groups: [ + { group: "Overview", pages: ["payments/api-reference/index", "payments/api-reference/versioning"] }, + ...groups, ], }; if (tag) entry.tag = tag; @@ -198,8 +178,14 @@ async function buildNavigation( ...previewVersions.map((v) => makeVersionEntry(v, { tag: "Preview" })), ]); + const apiReferenceTab = { tab: "API Reference", icon: "code", versions }; + + const tabs = currentConfig.navigation.tabs.map((t) => + t.tab === "API Reference" ? apiReferenceTab : t + ); + return { - versions, + tabs, global: currentConfig.navigation.global, }; } @@ -208,14 +194,14 @@ function buildManagedRedirects(latestStableVersion) { if (!latestStableVersion) return []; return [ { - source: "/api-reference/latest/:slug*", - destination: `/api-reference/${latestStableVersion}/:slug*`, + source: "/payments/api-reference/latest/:slug*", + destination: `/payments/api-reference/${latestStableVersion}/:slug*`, }, ]; } const MANAGED_REDIRECT_SOURCES = new Set([ - "/api-reference/latest/:slug*", + "/payments/api-reference/latest/:slug*", ]); function mergeRedirects(existing, latestStableVersion) { @@ -232,14 +218,14 @@ async function main() { const allVersions = [...stable, ...preview]; validateVersions(allVersions); - const apiRefDir = join(ROOT, "api-reference"); + const apiRefDir = join(ROOT, "payments", "api-reference"); const apiDir = join(ROOT, "api"); const versionSet = new Set(allVersions); const staleRefs = (await readdir(apiRefDir, { withFileTypes: true })) .filter((e) => e.isDirectory() && !versionSet.has(e.name)) .map((e) => { - console.log(`Removed stale directory api-reference/${e.name}`); + console.log(`Removed stale directory payments/api-reference/${e.name}`); return rm(join(apiRefDir, e.name), { recursive: true }); }); diff --git a/security.mdx b/security.mdx new file mode 100644 index 0000000..cd2e6a9 --- /dev/null +++ b/security.mdx @@ -0,0 +1,28 @@ +--- +title: "Security Information" +--- + +Security is not just a feature but a fundamental aspect of WalletConnect's architecture. The infrastructure has undergone multiple rounds of third-party security reviews, audits, penetration testing, and threat modeling to ensure the highest standards of protection. Security is viewed as a continuously evolving discipline, with regular system audits to identify and address potential vulnerabilities. The entire WalletConnect system underwent Threat Modeling by Spearbit in 2024, and then an entire ecosystem audit in 2026 to reach SOC2 Type II Certification. + +## SOC2 Type II Certification + +WalletConnect underwent SOC2 Type II certification in 2026, demonstrating robust security controls and the ability to secure user data from unauthorized access - using it solely for its intended purpose and with confidentiality, as well as providing consistent availability on all systems and processing data appropriately and within time limits. As a Type II certification, WalletConnect displayed its security and data controls work over a period of time rather than simply on the spot. + +## SDK for Wallets + +WalletConnect's SDK for wallets is an open-source SDK, supporting multiple transport methods, from WebSockets to Universal Links. Its design philosophy prioritizes minimizing third-party dependencies to reduce the attack surface area. To ensure its reliability and security, the SDK for wallets was audited by Trail of Bits. The audit report is available here. This comprehensive security review covered the source code and included a lightweight Threat Model covering upstream and downstream dependencies. + +## Third-Party Reviews + +The security infrastructure of Reown has undergone multiple rounds of audits by independent security auditing firms, including Trail of Bits, Halborn, and Spearbit. These audits cover both AppKit and WalletKit, along with a comprehensive company-wide Threat Model. + +| Audit Scope | Auditor | Report | +| --- | --- | --- | +| WalletConnect SOC2 Type II Certification | AICPA | | +| WalletConnect Comprehensive Threat Model | Spearbit | View Report | +| SDK for Apps Embedded Wallet Integration Pentest | Halborn | View Report | +| SDK for wallets Security Review & Lightweight Threat Model | Trail of Bits | View Report | + +## Security disclosures + +WalletConnect maintains an active bug bounty program to encourage security researchers to responsibly disclose vulnerabilities and help strengthen the systems. To report a vulnerability, please fill out the disclosure form here. diff --git a/snippets/chainlist.mdx b/snippets/chainlist.mdx new file mode 100644 index 0000000..86584f1 --- /dev/null +++ b/snippets/chainlist.mdx @@ -0,0 +1,80 @@ +export const ChainList = () => { + let chains = []; + let filteredChains = []; + + if (typeof document !== "undefined") { + fetch( + "https://explorer-api.walletconnect.com/v3/chains?projectId=8e998cd112127e42dce5e2bf74122539" + ) + .then((response) => response.json()) + .then((data) => { + chains = Object.keys(data.chains).map((key) => ({ + name: data.chains[key].name, + namespace: key, + })); + filteredChains = [...chains]; + renderChains(filteredChains); + + const searchInput = document.querySelector(".search-bar"); + if (searchInput) { + searchInput.addEventListener("input", (event) => { + const query = event.target.value.toLowerCase(); + filteredChains = chains.filter((chain) => + chain.name.toLowerCase().includes(query) + ); + renderChains(filteredChains); + }); + } + }) + .catch((error) => console.error(error)); + } + + const renderChains = (chains) => { + const container = document.querySelector(".chain-card-container"); + if (container) { + container.innerHTML = ""; + chains.forEach((chain) => { + const card = document.createElement("button"); + card.className = ` + flex items-center justify-center + border border-gray-500 p-2 text-center + w-full dark:bg-gray-600 dark:text-white h-20 + `; + card.innerText = chain.name; + card.onclick = () => { + navigator.clipboard.writeText(chain.namespace); + card.innerText = "Chain ID copied!"; + setTimeout(() => { + card.innerText = chain.name; + }, 3000); + }; + container.appendChild(card); + }); + } + }; + + return ( +
+ +
+
+ ); +}; diff --git a/snippets/cloud-banner.mdx b/snippets/cloud-banner.mdx new file mode 100644 index 0000000..8d22689 --- /dev/null +++ b/snippets/cloud-banner.mdx @@ -0,0 +1,12 @@ + + +**Don't have a project ID?** + +Head over to WalletConnect Dashboard and create a new project now! + + + + diff --git a/snippets/cloud/analytics.mdx b/snippets/cloud/analytics.mdx new file mode 100644 index 0000000..f666049 --- /dev/null +++ b/snippets/cloud/analytics.mdx @@ -0,0 +1,300 @@ +--- +title: 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. | diff --git a/snippets/cloud/explorer-submission.mdx b/snippets/cloud/explorer-submission.mdx new file mode 100644 index 0000000..7fdf8e5 --- /dev/null +++ b/snippets/cloud/explorer-submission.mdx @@ -0,0 +1,106 @@ +--- +title: Explorer Submission +--- + + +**Note** + +Submitting a project to the WalletConnect Dashboard Explorer is recommended but optional. You can still use WalletConnect services without submitting your project. +However, doing so ensures that your project is listed under [WalletGuide](https://walletguide.walletconnect.network/?utm_source=walletconnect-docs&utm_medium=cloud&utm_campaign=explorer-submission) and [Cloud Explorer API](/wallets/walletguide/explorer-api). + + +## Creating a New Project + +- Head over to [dashboard.walletconnect.com](https://dashboard.walletconnect.com/) and create a new project by clicking the "New Project" button in top right corner of the dashboard. +- Give a suitable name to your project, select whether its an App or Wallet and click the "Create" button. (You can change this later) + + + + + +## Project Details + +- Go to the "Explorer" tab and fill in the details of your project. + + + + + +| Field | Description | Required | +|------------------------------|-------------------------------------------------------------------------------------------------------------------------|----------| +| **Name** | The name to display in the explorer | Yes | +| **Description** | A short description explaining your project (dapp/wallet) | Yes | +| **Type** | Whether your project is a dapp or a wallet | Yes | +| **Category** | Appropriate category for your project. This field is dependent on the type of your project | Yes | +| **Homepage** | The URL of your project | Yes | +| **Web App** | The URL of your web app. This field is only applicable for dapps | Yes | +| **Chains** | Chains supported by your project | Yes | +| **Logo** | The logo of your project. Further requirements are provided in the explorer submission form | Yes | +| **Testing Instructions** | Instructions on how to test your WalletConnect Integration | Yes | +| **Download Links** | Links to download your project (if applicable) | No | +| **Mobile Linking** | Required for mobile wallets targeting AppKit. Deep Link is recommended over Universal Link | No | +| **Desktop Linking** | Required for desktop wallets targeting AppKit. | No | +| **Injected Wallet Identifiers** | Required for injected wallets targeting AppKit. RDNS (from EIP-6963 metadata) is recommended over Provider Flags (Legacy) | No | +| **Metadata** | User-facing UI metadata for your project. Only Short Name is required. | No | + + +## Project Submission + +- Once you've filled the applicable fields, click the "Submit" button to submit your project for review. Alternatively, you can save your changes and submit later. Additional information will be visible in the modal that appears after clicking the "Submit" button. + + + + + +## How do we test wallets? + +In order to offer a great user experience in our APIs and SDKs every Cloud submission goes through a QA process to make sure that the integration of the WalletConnect protocol is working correctly. + +The following list details our QA flow and how to reproduce it: + +| Test Case | Steps | Expected Results | +|-----------|-------|-----------------| +| **Set Up** | 1. Download the wallet
2. Install the wallet app
3. Sign up for an account with the wallet app
4. Create one or more accounts | 1. N/A
2. The app is installed
3. I have an account
4. I have one or more accounts | +| **Connect to dapp via web browser** | 1. Open the Reown connection page [https://appkit-lab.reown.com/](https://appkit-lab.reown.com/) from a PC
2. Press on the “Connect Wallet” button and select the Reown option.
3. Open the wallet app and use the scan QR option to connect.
4. Accept on the wallet the connection request | 1. The app has been correctly set-up
2. A modal with wallet options is opened
3. A QR code is shown on the website and the wallet is able to scan it.
4. The connection is successfully established. The wallet data is now shown on the website. | +| **Connect to dapp via mobile browser (Deep-link)** | 1. Open [https://appkit-lab.reown.com/](https://appkit-lab.reown.com/) in your mobile device.
2. Select one of the default options (e.g. Wagmi for EVM chains). Press the "Custom Wallet" button from the navbar. Fill in the wallet’s name and its deeplink (Mobile Link) in the “Add a Custom Wallet” form. Press “Add Wallet”. After the website reloads, press the “Connect Wallet” button and select the newly created wallet.
3. Accept the connection request in the wallet application. | 1. N/A
2. A form should show up on the website to fill in the wallet’s data. After the changes are applied, the modal should show the newly created wallet on the main view.
3. The user should be redirected to the wallet application and a modal with a connection request should show up on the wallet application. The wallet should connect successfully. On Android devices, the user should be redirected back to the website after accepting the connection request. | +| **Switch chains - dapp side** | 1. Once the wallet is connected, press on the modal button on the top right of the website.
2. Press the first button of the modal to switch the chain.
3. Select any available chain, close the modal, and press the “Send Transaction” button | 1. A modal with the account information should pop up on the website.
2. A new view with supported chains should show up.
3. The transaction request that pops up on the wallet should show in their information the correct chain that was previously selected. | +| **Switch Chains - wallet side (if supported)** | 1. Check if the wallet supports chain switching. If so, select a different chain from the connected one. | 1. The chain change should be reflected on the website. The first card shows the current chain ID. | +| **Accounts Switching - wallet side** | 1. In the wallet app, switch from one account to another. | 1. The account switch event should be reflected in the modal’s account view on the website. | +| **Disconnect a wallet** | 1. Select the "Disconnect" button from the Wallet App (Ideally, wallets should have a section where users can see all their existing dApp connections and manage/disconnect from dApps in one spot—this is not always true, so if not possible, just skip this).
2. Repeat the above steps and press the "Disconnect" button from the dApp (this should always be available). | 1. The related session should disappear from the dApp and the Wallet App.
2. The related session should disappear from the dApp and the Wallet App. | +| **Verify API** | 1. Open [https://malicious-app-verify-simulation.vercel.app/](https://malicious-app-verify-simulation.vercel.app/)
2. Select a supported chain by the wallet (some wallets don’t support testnets) and press the “Connect” button.
3. Scan with the wallet the generated QR code. | 1. N/A
2. A modal should show up with a QR code to scan.
3. The connection request in the wallet should flag the website as malicious. | + + +### Chain Specific + +The following test cases only apply for wallets supporting a particular set of chains. + + + + +| Test Case | Steps | Expected Results | +|-----------|-------|-----------------| +| **Supporting personal_sign** | 1. Connect the wallet.
2. Press the “Sign Message” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | +| **Supporting eth_signTypedData_v4** | 1. Connect the wallet.
2. Press the “Sign Typed Data” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | +| **Supporting eth_sendTransaction** | 1. Connect the wallet.
2. Press the “Send Transaction” button. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature. | + + +
+ + + +| Test Case | Steps | Expected Results | +|-----------|-------|-----------------| +| **Supporting solana_signMessage** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Message” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | +| **Supporting solana_signTransaction** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Transaction” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | +| **Supporting v0 Transactions** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Versioned Transaction” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | + + +
+
+ +## What's Next? + +Now depending on whether or not your submission met all parameters, you will receive an email from the WalletConnect team with the status of your submission. This change will also be reflected with more directions in the "Explorer" tab of your project. +If your submission was not accepted, you can make the necessary changes and resubmit your project for review. The reason for rejection will be mentioned in the email and in the "Explorer" tab of your project. + +In case of any questions, feel free to ask on [Github Discussions](https://github.com/orgs/WalletConnect/discussions/categories/explorer-support) diff --git a/snippets/cloud/relay.mdx b/snippets/cloud/relay.mdx new file mode 100644 index 0000000..1cc4355 --- /dev/null +++ b/snippets/cloud/relay.mdx @@ -0,0 +1,69 @@ +--- +title: 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 +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://] { {description}

- {model} + {model && ( + {model} + )} {type}
diff --git a/snippets/walletkit/shared/chain-abstraction/error-handling.mdx b/snippets/walletkit/shared/chain-abstraction/error-handling.mdx new file mode 100644 index 0000000..3a87b8e --- /dev/null +++ b/snippets/walletkit/shared/chain-abstraction/error-handling.mdx @@ -0,0 +1,29 @@ +## 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. diff --git a/snippets/walletkit/shared/chain-abstraction/intro.mdx b/snippets/walletkit/shared/chain-abstraction/intro.mdx new file mode 100644 index 0000000..3262e93 --- /dev/null +++ b/snippets/walletkit/shared/chain-abstraction/intro.mdx @@ -0,0 +1,25 @@ + +💡 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 + + + + \ No newline at end of file diff --git a/snippets/walletkit/shared/mobile-linking.mdx b/snippets/walletkit/shared/mobile-linking.mdx new file mode 100644 index 0000000..0d2717a --- /dev/null +++ b/snippets/walletkit/shared/mobile-linking.mdx @@ -0,0 +1,12 @@ +### 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/ +- For Solana: https://appkit-lab.reown.com/library/solana/ + +2. Click the "Custom Wallet" button and fill in the form with your wallet information. The website will reload and your wallet will be stored locally. +3. Click the "Connect Wallet" button and choose your mobile wallet. It _should_ automatically open and redirect to your wallet. + +Learn more about mobile linking in the [Best Practices section](/wallets/android/best-practices#2-mobile-linking). \ No newline at end of file diff --git a/snippets/walletlist.mdx b/snippets/walletlist.mdx new file mode 100644 index 0000000..9a0a491 --- /dev/null +++ b/snippets/walletlist.mdx @@ -0,0 +1,89 @@ +export const WalletList = () => { + let wallets = []; + let originalWalletsArray = []; + + if (typeof document !== "undefined") { + fetch( + "https://explorer-api.walletconnect.com/v3/wallets?projectId=8e998cd112127e42dce5e2bf74122539" + ) + .then((response) => response.json()) + .then((data) => { + wallets = data.listings; + originalWalletsArray = Object.keys(data.listings).map((key) => ({ + ...data.listings[key], + namespace: key, + })); + renderWallets(wallets); + + const searchInput = document.querySelector(".search-bar"); + if (searchInput) { + searchInput.addEventListener("input", (event) => { + const query = event.target.value.toLowerCase(); + const filteredwallets = Object.fromEntries( + Object.entries(wallets).filter(([_, wallet]) => + wallet.name.toLowerCase().includes(query) + ) + ); + renderWallets(filteredwallets); + }); + } + }) + .catch((error) => console.error(error)); + } + + const renderWallets = (wallets) => { + const container = document.querySelector(".wallet-card-container"); + if (container) { + container.innerHTML = ""; + Object.keys(wallets).forEach((key) => { + const wallet = wallets[key]; + const card = document.createElement("button"); + card.className = ` + flex flex-col items-center justify-center + border border-gray-500 p-2 text-center + w-full dark:bg-gray-600 dark:text-white h-20 + `; + card.innerHTML = ` + ${wallet.name} + ${wallet.name} + `; + card.onclick = () => { + navigator.clipboard.writeText(wallet.id); + card.innerHTML = "Wallet ID copied!"; + setTimeout(() => { + card.innerHTML = ` + ${wallet.name} + ${wallet.name} + `; + }, 3000); + }; + container.appendChild(card); + }); + } + }; + + return ( +
+ +
+
+ ); +}; \ No newline at end of file diff --git a/wallets/android/best-practices.mdx b/wallets/android/best-practices.mdx new file mode 100644 index 0000000..ce0ccb2 --- /dev/null +++ b/wallets/android/best-practices.mdx @@ -0,0 +1,234 @@ +--- +title: 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 +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 +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 +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 + 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 + 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 +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 +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 +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 +val walletDelegate = object : WalletKit.WalletDelegate { + override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) { + //Here this event is triggered when a connection state has changed + } + ...other callbacks +} +WalletKit.setWalletDelegate(walletDelegate) +``` + +### Expected User flow + +### Connection State + + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/connection_state.gif) + diff --git a/wallets/android/chain-abstraction.mdx b/wallets/android/chain-abstraction.mdx new file mode 100644 index 0000000..7cee853 --- /dev/null +++ b/wallets/android/chain-abstraction.mdx @@ -0,0 +1,141 @@ +--- +title: Chain Abstraction +--- + +import HowItWorks from "/snippets/walletkit/shared/chain-abstraction/intro.mdx"; +import ErrorHandling from "/snippets/walletkit/shared/chain-abstraction/error-handling.mdx"; + + + +## 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 +@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 +@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 + 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. + + + +## Testing + +To test Chain Abstraction, you can use the [AppKit laboratory](https://appkit-lab.reown.com/library/wagmi/) and try sending any supported [tokens](/wallets/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction supported wallet. +You can also use this [sample wallet](https://appdistribution.firebase.dev/i/076a3bc9669d3bee) for testing. + + + +## ProGuard rules + +If you encounter issues with minification, add the below rules to your application: + +``` +-keepattributes *Annotation* + +-keep class com.sun.jna.** { *; } +-keepclassmembers class com.sun.jna.** { + native ; + *; +} + +-keep class uniffi.** { *; } + +# Preserve all public and protected fields and methods +-keepclassmembers class ** { + public *; + protected *; +} + +-dontwarn uniffi.** +-dontwarn com.sun.jna.** +``` \ No newline at end of file diff --git a/wallets/android/cloud/analytics.mdx b/wallets/android/cloud/analytics.mdx new file mode 100644 index 0000000..f78ba0b --- /dev/null +++ b/wallets/android/cloud/analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +--- + +import Analytics from "/snippets/cloud/analytics.mdx"; + + diff --git a/wallets/android/cloud/explorer-submission.mdx b/wallets/android/cloud/explorer-submission.mdx new file mode 100644 index 0000000..cd1e47b --- /dev/null +++ b/wallets/android/cloud/explorer-submission.mdx @@ -0,0 +1,7 @@ +--- +title: Explorer Submission +--- + +import ExplorerSubmission from "/snippets/cloud/explorer-submission.mdx"; + + \ No newline at end of file diff --git a/wallets/android/cloud/relay.mdx b/wallets/android/cloud/relay.mdx new file mode 100644 index 0000000..5f9e1c0 --- /dev/null +++ b/wallets/android/cloud/relay.mdx @@ -0,0 +1,7 @@ +--- +title: Relay +--- + +import Relay from "/snippets/cloud/relay.mdx"; + + diff --git a/wallets/android/eip5792.mdx b/wallets/android/eip5792.mdx new file mode 100644 index 0000000..6818c5c --- /dev/null +++ b/wallets/android/eip5792.mdx @@ -0,0 +1,271 @@ +--- +title: Wallet Call API +--- + +WalletConnect supports [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability), which defines new JSON-RPC methods that enable apps to ask a wallet to process a batch of onchain write calls and to check on the status of those calls. +Applications can specify that these onchain calls be executed taking advantage of specific capabilities previously expressed by the wallet; an additional, a novel wallet RPC is defined to enable apps to query the wallet for those capabilities. + +- `wallet_sendCalls`: Requests that a wallet submits a batch of calls. +- `wallet_getCallsStatus`: Returns the status of a call batch that was sent via wallet_sendCalls. +- `wallet_showCallsStatus`: Requests that a wallet shows information about a given call bundle that was sent with wallet_sendCalls. +- `wallet_getCapabilities`: This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication). + +## Usage + + + + ## 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 +"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 +"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 +"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 + { + "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 + { + "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 + { + "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 + { + "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 + { + "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 + { + "receipts": [/* single receipt or array of receipts */], + "atomic": true + } + ``` + + #### For Non-Atomic Execution + ```json + { + "receipts": [/* array of receipts for all transactions */], + "atomic": false + } + ``` + + + +## References +- EIP-5792: https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability +- CAIP-25 namespaces: https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md diff --git a/wallets/android/installation.mdx b/wallets/android/installation.mdx new file mode 100644 index 0000000..5ff30f5 --- /dev/null +++ b/wallets/android/installation.mdx @@ -0,0 +1,50 @@ +--- +title: Installation +--- + +Add the `jitpack.io` Maven repository to your `root/build.gradle.kts` file. For example: + +```gradle +allprojects { + repositories { + mavenCentral() + maven { url "https://jitpack.io" } + } +} +``` + +In `app/build.gradle.kts` add the WalletKit package and its dependencies: + +```gradle +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. diff --git a/wallets/android/link-mode.mdx b/wallets/android/link-mode.mdx new file mode 100644 index 0000000..3b21ad0 --- /dev/null +++ b/wallets/android/link-mode.mdx @@ -0,0 +1,57 @@ +--- +title: Link Mode +--- + +The Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallets/android/one-click-auth) requests and session requests over Universal Links, reducing the need for a WebSocket connection with the Relay. This significantly enhances the user experience when connecting native dApps to native wallets by reducing the latency associated with network connections, especially when the user has an unstable internet connection. + +To support Link Mode add a universal link for your wallet in Cloud project configuration dashboard, configure your AppMetaData `appLink` with a valid universal link and set the `linkMode` property to `true`: + + +Make sure that [1-Click Auth](/wallets/android/one-click-auth) is implemented before enabling Link Mode. + + +```kotlin {3-4} + 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 +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). diff --git a/wallets/android/mobile-linking.mdx b/wallets/android/mobile-linking.mdx new file mode 100644 index 0000000..5a8f99a --- /dev/null +++ b/wallets/android/mobile-linking.mdx @@ -0,0 +1,190 @@ +--- +title: Mobile Linking +--- + +import HowToTest from "/snippets/walletkit/shared/mobile-linking.mdx"; + + + +This feature is only relevant to native platforms. + + + +## Usage + +Mobile Linking allows your wallet to automatically redirect back to the Dapp allowing for less user interactions and hence a better UX for your users. + +### Establishing Communication Between Mobile Wallets and Apps + +When integrating a wallet with a mobile application, it's essential to understand how they communicate. The process involves two main steps: + +1. **QR Code Handshake:** The mobile app (Dapp) generates a unique URI (Uniform Resource Identifier) and displays it as a QR code. This URI acts like a secret handshake. When the user scans the QR code using their wallet app, they establish a connection. It's like saying, "Hey, let's chat!" +2. **Deep Links and Universal Links:** The URI from the QR code allows the wallet app to create a [deep link](https://support.google.com/google-ads/answer/10023042?hl=en#:~:text=Deep%20links%20send%20mobile%20device,%2C%20Shopping%2C%20and%20Display%20campaigns.) or [universal link](https://developer.apple.com/ios/universal-links/). These links work on both Android and iOS. They enable seamless communication between the wallet and the app. + + + +**Developers should prefer Deep Linking over Universal Linking.** + +Universal Linking may redirect the user to a browser, which might not provide the intended user experience. Deep Linking ensures the user is taken directly to the app. + + + +### Key Behavior to Address + +In some scenarios, wallets use redirect metadata provided in session proposals to open applications. This can cause unintended behavior, such as: + +Redirecting to the wrong app when multiple apps share the same redirect metadata (e.g., a desktop and mobile version of the same Dapp). +Opening an unrelated application if a QR code is scanned on a different device than where the wallet is installed. + +#### Recommended Approach + +To avoid this behavior, wallets should: + +- **Restrict Redirect Metadata to Deep Link Use Cases**: Redirect metadata should only be used when the session proposal is initiated through a deep link. QR code scans should not trigger app redirects using session proposal metadata. + +The connection and sign request flows are similar across platforms. + +### Connection Flow + +- **Dapp Prompts User:** The Dapp asks the user to connect. +- **User Chooses Wallet:** The user selects a wallet from a list of compatible wallets. +- **Redirect to Wallet:** The user is redirected to their chosen wallet. +- **Wallet Approval:** The wallet prompts the user to approve or reject the session (similar to granting permission). +- **Return to Dapp:** + - **Manual Return:** The wallet asks the user to manually return to the Dapp. + - **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. +- **User Reunites with Dapp:** After all the interactions, the user ends up back in the Dapp. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking-light.png) + + +### Sign Request Flow + +When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs: + +- **Automatic Redirect:** The Dapp automatically sends the user to their previously chosen wallet. +- **Approval Prompt:** The wallet asks the user to approve or reject the request. +- **Return to Dapp:** + - **Manual Return:** The wallet asks the user to manually return to the Dapp. + - **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. +- **User Reconnects:** Eventually, the user returns to the Dapp. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking_sign-light.png) + + +## Platform preparations + +In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to add your own wallet to the Explorer by login to your [WalletConnect Dashboard](https://dashboard.walletconnect.com/sign-in) account, declare a deep link and define an [``](https://developer.android.com/training/app-links/deep-linking#adding-filters) in your wallet's Manifest.xml with the same deep link added in Explorer: + +```xml + + + + + + +``` + + + +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 + + + + + +## 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 +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 + WalletKit.approveSession(approveProposal, + onSuccess = { + // trigger deeplink: proposal.redirect + } +) +``` + +Redirect when responding to a request: + +```kotlin +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 + +#### 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 +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 +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 + +#### References + +- https://developer.android.com/guide/navigation/navigation-deep-link#implicit +- https://developer.android.com/training/app-links#deep-links diff --git a/wallets/android/notifications/notify/installation.mdx b/wallets/android/notifications/notify/installation.mdx new file mode 100644 index 0000000..98bd58b --- /dev/null +++ b/wallets/android/notifications/notify/installation.mdx @@ -0,0 +1,31 @@ +--- +title: Installation +--- + +Add the `jitpack.io` Maven repository to your `root/build.gradle.kts` file. For example: + +```gradle +allprojects { + repositories { + mavenCentral() + maven { url "https://jitpack.io" } + } +} +``` + +In `app/build.gradle.kts` add the notify package and its dependencies: + +```gradle +implementation(platform("com.reown:android-bom:release_version")) +implementation("com.reown:android-core") +implementation("com.reown:notify") +``` + +#### Requirements + +- Android API level minimum 23 +- Java minimum version 11 + +## Next Steps + +Now that you've installed WalletConnect Notify, you're ready to start integrating it. The next section will walk you through the process of setting up your project to use the Notify API. diff --git a/wallets/android/notifications/notify/overview.mdx b/wallets/android/notifications/notify/overview.mdx new file mode 100644 index 0000000..6879154 --- /dev/null +++ b/wallets/android/notifications/notify/overview.mdx @@ -0,0 +1,26 @@ +--- +title: Overview +--- + + +For those integrating notifications related to wallet pairing and sign requests, please check [here](../push). + + +The WalletConnect Notify API is designed to enhance the interaction between wallet users and dapps by offering a robust notification system. This API empowers wallet developers to implement a dynamic notification experience directly within their wallets. It provides the functionality for users to opt-in to notifications, ensuring they stay informed about critical events and interactions. + +The Notify API is versatile, with support for both iOS and Android platforms, making it an ideal choice for cross-platform wallet applications. + +Coupled with the AppKit Notifications, the Notify API forms part of a comprehensive toolkit that enables seamless integration of web3 communication and messaging features into dapps. This ensures a more connected and interactive experience for users in the decentralized ecosystem. + +## Features + +Some of the key features of the Notify API include: + +- **Push Notifications for Desktop and Native Platforms**: This feature enables dapps to directly send vital notifications to user wallets, ensuring timely and relevant communication. +- **Robust Spam Protection**: Users have complete authority over which dapps can send them notifications, effectively eliminating any unsolicited messages from unknown sources. Furthermore, users can fine-tune their preferences to only receive notifications types they are interested in, like new features or some important events occurrence. +- **Chain Agnostic Architecture**: The Notify API is built to be compatible with any blockchain, allowing seamless multi-chain support without the need for writing additional integration code. **As of November 2023, the Notify Server and Clients are equipped to support EVM chains. Plans to extend support to non-EVM chains are in progress and are a significant part of our upcoming development roadmap.** + +_Example integration_ + + + diff --git a/wallets/android/notifications/notify/resources.mdx b/wallets/android/notifications/notify/resources.mdx new file mode 100644 index 0000000..58b79f4 --- /dev/null +++ b/wallets/android/notifications/notify/resources.mdx @@ -0,0 +1,19 @@ +--- +title: Resources +--- + +Valuable assets for developers interested in integrating Notify API into their wallet. + +- [Web3Inbox.com app](https://app.web3inbox.com) - Inbox web app that simulates wallet experience. +- [GM dapp](https://gm.walletconnect.com/) - Example dapp that sends notification every hour. +- [GM hackers](https://github.com/WalletConnect/gm-hackers) - Template used in hackathons sponsored by WalletConnect. + +## Wallet Resources + +To check more in details go and visit our [WalletKit Kotlin implementation app](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/develop/sample/wallet). Sample Wallet .apk files can be found under the latest release tag in [Kotlin's V2 repository](https://github.com/WalletConnect/WalletConnectKotlinV2/tags) + +If you need to test your app's integration, you can use one [our GM dapp.](https://gm.walletconnect.com/) + +## Need Technical Support? + +If you require technical support along the way, please drop a message on the [WalletConnect GitHub](https://github.com/orgs/WalletConnect/discussions/) and our team will get back to you as soon as possible. diff --git a/wallets/android/notifications/notify/spam-protection.mdx b/wallets/android/notifications/notify/spam-protection.mdx new file mode 100644 index 0000000..d56bbfa --- /dev/null +++ b/wallets/android/notifications/notify/spam-protection.mdx @@ -0,0 +1,27 @@ +--- +title: Spam Protection +--- + +Users play a critical role in web3. That’s why, with WalletKit Notifications, we’re committed to ensuring users can enjoy a safe, seamless, and reliable experience that puts them in the driver’s seat. As part of that pledge, Web3Inbox provides a number of user-first, anti-spam features and elements that ensure users are always in control of their web3 communications. + +## How are users protected from spam with WalletKit Notifications? + +### Becoming a WalletKitNotifications customer + +When a wallet offers app notifications to their users via WalletKit Notifications, the feature will always be optional. If users decide they want to receive notifications from selected apps via their wallet, they’ll be able to ‘opt-in’ and subscribe to an app’s notifications by signing a message request. Similarly, when accessing notifications through the [Web3Inbox.com app](https://app.web3inbox.com), users will be met with the same request for each application they choose to subscribe to. This feature not only enables users to experience a customized, ‘app-by-app’ approach to staying connected in web3, but also ensures they only ever hear from the apps they choose to — no unsolicited notifications or spam from unknown senders. Its their curated inbox, connected with only those they choose. + +### Setting customized notification preferences + +Once users have subscribed to their chosen apps, they have the option to define and set which types of notifications they receive from those apps. For example, a user may wish to receive only information regarding changes to their portfolio from a DEX, or, they might want to receive notifications from an NFT marketplace — but only notifications regarding their own NFT collections. In these scenarios, they’ll have the ability to disable other notification types, like marketing updates, and ensure their feed is curated to show only information that’s meaningful to them. As apps set their own notification types, they have unlimited optionality to really build out a notification structure they know can support their users’ needs — no ‘one size fits all’ approach, but a personable, community-oriented structure that puts both app and user needs’ at the forefront of communication. + +### Rate limiting + +Apps are limited to a maximum number of notifications they’re able to send to their community. Specifically, apps may send accounts notifications twice an hour on average, but may exceed that average in bursts of up to 50 at a time. + +## Our continued pledge on spam protection + +We’re constantly working on improving and growing our products, and we have a number of impactful anti-spam features and functions in the works set to increase the overall protection and user experience of Web3Inbox users: + +### User reporting + +Users will have the ability to report applications that appear to be acting or engaging with their community in a malicious or suspicious manner. Projects that are flagged as malicious may be removed from the Web3Inbox discover page and have notification functionality disabled. diff --git a/wallets/android/notifications/notify/usage.mdx b/wallets/android/notifications/notify/usage.mdx new file mode 100644 index 0000000..be778aa --- /dev/null +++ b/wallets/android/notifications/notify/usage.mdx @@ -0,0 +1,339 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + + +In this section, we showcase the aspects of using the Notify API. We'll guide you through the initial steps of initializing the Notify client and logging in a blockchain account. You'll also learn how to manage your subscriptions and messages. Additionally, we cover the process of setting up and displaying push notifications on your preferred platform. To ensure a good user experience, we include best practices for spam protection, helping you to enable the users to maintain control over the notifications wallet receives. + +## 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 Notify Client instance and initializing it with a projectId from [[WalletConnect Dashboard](https://dashboard.walletconnect.com/). +- [Account login](#account-login): + A SIWE message must be signed by the user in order to authorize the client to use Notify API +- [Subscribing to a new dapp](#subscribing-to-a-new-dapp): + Opt-in to receive notifications from dapp +- [Fetching active subscriptions](#fetching-active-subscriptions): + Get active subscriptions +- [Fetching subscription’s notification](#fetching-subscriptions-notifications): + Get notifications of a subscription +- [Fetching available notification types](#fetching-available-notification-types): + Get latest notification types +- [Updating subscriptions notification settings](#updating-subscriptions-notification-settings): + Change allowed notification types sent by dapp +- [Unsubscribe from a dapp](#unsubscribe-from-a-dapp): + Opt-out from receiving notifications from a dapp +- [Account logout](#account-logout): + To stop receiving notifications to this client, accounts can logout of using Notify API +- [Push Notification best practices](#push-notification-best-practices): + Guidelines on how to implement Push Notifications across different platforms +- [Firebase Cloud Messaging setup **(Android)**](#firebase-cloud-messaging-setup): + Configuring Android app in order to decrypt notifications +- [NotifyClient.Delegate **(Android)**](#notifyclientdelegate): + Setting and overriding functions through NotifyDelegate. + +## Initialization + + + +To initialize the Notify client, create a `Notify.Params.Init` object in the Android Application class with the Core Client passed as a parameter. The `Notify.Params.Init` object will then be passed to the `Notify.initialize` function. There is also an `onError` callback that will need to be provided which will return an instance of `Notify.Model.Error` if there's an issue initializing the client. + +**Note:** The CoreClient used here will be the same instance of the CoreClient used in other WalletConnect Kotlin SDKs + +```kotlin +val projectId = PROJECT_ID +val appMetaData = Core.Model.AppMetaData( + name = /* The name of your project as a String */, + description = /* A description of your project as a String */, + url = /* A url for your project as a String */, + icons = /* A list of URLs to icons related to your project as Strings */, + redirect = /* A redirect URI used by Dapps to deeplink back to your wallet. This is a String value */ +) + +CoreClient.initialize(projectId = projectId, connectionType = ConnectionType.AUTOMATIC, application = this, metaData = appMetaData) + +Notify.initialize(init = Notify.Params.Init(core = CoreClient) { error: Notify.Model.Error -> + // Error will be thrown if there's an issue during initialization +} +``` + +## Account login + +In order to register account in Notify API to be able to subscribe to any dapp to start receiving notifications, account needs to sign SIWE message to prove ownership. Developers can check if an account is registered by calling **`isRegistered()`** function. If the account is not registered, developers should call **`prepareRegistration()`** and then **`register()`** function to register the account. + +Snippet below shows how to check if an account is registered and how to register an account if it's not registered yet. Developers could use `CacaoSigner` to sign the message or use their own signing method. + +```kotlin +val account: String = ""// The CAIP-10 account i.e. "eip155:1:0xAbC1234567890DefABC1234567890dEFABC12345" +val domain = BuildConfig.APPLICATION_ID + +// Caution: This function is blocking and runs on the current thread. It is advised that this function be called from background operation +val isRegistered = NotifyClient.isRegistered(params = Notify.Params.IsRegistered(account = account, domain = domain)) + +if (!isRegistered) { + NotifyClient.prepareRegistration( + params = Notify.Params.PrepareRegistration(account = account, domain = domain), + onSuccess = { cacaoPayloadWithIdentityPrivateKey, message -> + + // Pick one of the following methods to sign the message: + + // 1. Using CacaoSigner to sign the message + val signature = CacaoSigner.sign( + message, + PRIVATE_KEY, // Private key used to signing a message, + SignatureType.EIP191 + ) + + // 2. Alternatively, you can use your own signing method + /** Add imports: + import com.reown.android.cacao.signature.SignatureType + import com.reown.android.internal.common.signing.signature.Signature + import com.reown.android.internal.common.signing.signature.toCacaoSignature + */ + + val signature: String = // Here developers provide signed message using their own signing method + val cacaoSignature = Notify.Model.Cacao.Signature(SignatureType.EIP191.header, Signature.fromString(signature).toCacaoSignature()) + + // Once the message has been signed, call the register function + + NotifyClient.register( + params = Notify.Params.Register(cacaoPayloadWithIdentityPrivateKey = cacaoPayloadWithIdentityPrivateKey, signature = signature), + onSuccess = { + // Registration was successful + }, + onError = { + // There was an error while trying to register the account + } + ) + + }, + onError = { + // There was an error while trying to prepare the registration + } + ) +} else { + // Great! Account is already registered +} +``` + +## Subscribing to a new dapp + +To begin receiving notifications from a dapp, users must opt-in by subscribing. This subscription process grants permission for the dapp to send notifications to the user. These notifications can serve a variety of purposes, such as providing updates on the user's blockchain account activities or informing them about ongoing campaigns within the dapp. Upon initial subscription, clients will be automatically enrolled to receive all types of notifications as defined by the dapp at that moment. Users have the flexibility to modify their notification settings later, allowing them to tailor the types of alerts they receive according to their preferences. + +```kotlin +val appDomain: Uri = // Dapp uri. e.g. gm.walletconnect.com +val account: String = // CAIP-10 account +val timeout: Duration? = // Optional. Timeout - min 5 sec, max 60 sec, default 60 sec +val params = Notify.Params.Subscribe(appDomain, account, timeout) + +NotifyClient.subscribe(params = params).let { result -> + when (result) { + is Notify.Result.Subscribe.Success -> { + // callback for when the subscription request was successful + } + + is Notify.Result.Subscribe.Error -> { + // callback for when the subscription request has failed + } + + } +} +``` + +## Fetching active subscriptions + +To fetch the current list of subscriptions an account has, call **`getActiveSubscriptions()`**. + +Method will return a map with the topic as the key and `Notify.Model.Subscription` as the value. + +```kotlin +val account: String = // CAIP-10 account +val timeout: Duration? = // Optional. Timeout - min 5 sec, max 60 sec, default 60 sec +val params = Notify.Params.GetActiveSubscriptions(account, timeout) + +try { + val result: Map = NotifyClient.getActiveSubscriptions(params) +} catch (e: Exception) { + // callback for when the get active subscriptions request has failed +} +``` + +## Fetching subscription’s notifications + +To fetch subscription’s notifications by calling **`getNotificationHistory()`**. + +```kotlin +val topic: String = // active subscription topic +val limit: Int? = // Optional. Limit - min 1, max 50, default 10 +val startingAfter: String? = // Optional. Id of the notification to start after +val timeout: Duration? = // Optional. Timeout - min 5 sec, max 60 sec, default 60 sec + +val params = Notify.Params.GetNotificationHistory(topic, limit, startingAfter, timeout) + +NotifyClient.getNotificationHistory(params).let { result -> + when (result) { + is Notify.Result.GetNotificationHistory.Success -> { + // callback for when the get notification history request was successful + } + + is Notify.Result.GetNotificationHistory.Error -> { + // callback for when the get notification history request has failed + } + } +} +``` + +## Fetching available notification types + +Developers can fetch latest notification types specified by dapp by calling **`getNotificationTypes()`** function. + +Method will return a map with the notification type id as the key and `Notify.Model.NotificationType` as the value. + +```kotlin +val appMetadata: Core.Model.AppMetaData = // App Metadata could be fetched from NotifyClient.getActiveSubscriptions() +val appDomain: String = URI(appMetadata.url).host +val timeout: Duration? = // Optional. Timeout - min 5 sec, max 60 sec, default 60 sec + +val params = Notify.Params.NotificationTypes(appDomain, timeout) +try { + val result: Map = NotifyClient.getNotificationTypes(params) +} catch (e: Exception) { + // callback for when the get notification types request has failed +} +``` + +## Updating subscriptions notification settings + +Users can alter their notification settings to filter out unwanted alerts from a dapp. During this process, they review and select the types of notifications they wish to receive, based on the latest options provided by the dapp. Available notification types fetching is shown in the [next section](#fetching-available-notification-types). + +```kotlin +val topic: String = // active subscription topic +val scope: List = // list of notification types +val timeout: Duration? = // Optional. Timeout - min 5 sec, max 60 sec, default 60 sec +val params = Notify.Params.UpdateSubscription(topic, scope, timeout) + +NotifyClient.update(params).let { result -> + when (result) { + is Notify.Result.UpdateSubscription.Success -> { + // callback for when the update request was successful + } + is Notify.Result.UpdateSubscription.Error -> { + // callback for when the update request has failed + } + } +} +``` + +## Unsubscribe from a dapp + +To opt-out of receiving notifications from a dap, a user can decide to unsubscribe from dapp. + +```kotlin +val topic: String = // active subscription topic +val timeout: Duration? = // Optional. Timeout - min 5 sec, max 60 sec, default 60 sec +val params = Notify.Params.DeleteSubscription(topic) + +NotifyClient.deleteSubscription(params).let { result -> + when (result) { + is Notify.Result.DeleteSubscription.Success -> { + // callback for when the delete request was successful + } + + is Notify.Result.DeleteSubscription.Error -> { + // callback for when the delete request has failed + } + } +} +``` + +## Account logout + +If an account is removed from the client or a user no longer wants to receive notifications for this account, you can logout the account from Notify API by calling **`unregister()`**. This will remove all subscriptions and messages for this account from the client’s storage. + +```kotlin +val params = Notify.Params.Unregistration(/*CAIP-10 account*/) +NotifyClient.unregister( + params, + onSuccess = { + // callback for when the unregistration was successful + }, + onError = { error -> + // callback for when the unregistration has failed + } +) +``` + +## Push Notification best practices + +To create a good user experience and to guide users into unsubscribing from the correct dapp, there are certain best practices when displaying push notifications. + +`Core.Model.Message` contains a `type` field, which is a unique id of the notification type. It is recommended to use this field as a notification channel id. By doing so it will create a channel for each notification type. To allow users to granularly control which notifications they want to receive within system settings, it is recommended to create a separate channel for every dapp and every notification type they might have. By doing so user would be able to turn off notifications for specific notification type per every subscribed dapp. + +```kotlin +class SampleFirebaseService: PushMessagingService() { + //... + override fun onMessage(message: Core.Model.Message, originalMessage: RemoteMessage) { + if (message is Core.Model.Message.Notify) { + val account: String = // CAIP-10 account + val appMetadata = NotifyClient.getActiveSubscriptions(Notify.Params.GetActiveSubscriptions(account))[topic]?.metadata + ?: throw IllegalStateException("No active subscription for topic: $topic") + + val appDomain = URI(appMetadata.url).host + ?: throw IllegalStateException("Unable to parse domain from $appMetadata.url") + + val notificationType = NotifyClient.getNotificationTypes(Notify.Params.GetNotificationTypes(appDomain))[channelId] + ?: throw IllegalStateException("No notification type for topic:${topic} and type: $channelId") + + val channelName = appMetadata.name + ": " + notificationType.name + val channelId = message.type + + val notificationBuilder = NotificationCompat.Builder(this, channelId) + .setContentTitle(message.title) + .setSmallIcon(android.R.drawable.ic_popup_reminder) // specify icon for notification + .setContentText(message.body) + .setAutoCancel(true) // clear notification after click + .setSound(defaultSoundUri) // specify sound for notification + .setContentIntent(pendingIntent) // specify pendingIntent + + // Since android Oreo notification channel is needed. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH) + notificationManager.createNotificationChannel(channel) + } + + notificationManager.notify(message.hashCode(), notificationBuilder.build()) // specify id of notification + } + } +//... +``` + +### Firebase Cloud Messaging setup + +To setup Firebase Cloud Messaging please follow our [Push Notifications docs](../push). + +### NotifyClient.Delegate + +`NotifyClient` needs a `NotifyClient.Delegate` passed to it for it to be able to expose asynchronous updates sent from the dapp. It's recommended to set the delegate in the `onCreate` function of the `Application` class. + +```kotlin +val walletDelegate = object : NotifyClient.Delegate { + + override fun onNotifySubscription(notifySubscribe: Notify.Event.Subscription) { + // Triggered when a wallet initiated subscription has been created + } + + override fun onNotifyNotification(notifyNotification: Notify.Event.Notification) { + // Triggered when a message has been sent by the Dapp. The message contains the title, body, icon, and url + } + + override fun onError(error: Notify.Model.Error) { + // Triggered when there's an error inside the SDK + } +} + +NotifyClient.setDelegate(walletDelegate) +``` diff --git a/wallets/android/notifications/push.mdx b/wallets/android/notifications/push.mdx new file mode 100644 index 0000000..ca597ce --- /dev/null +++ b/wallets/android/notifications/push.mdx @@ -0,0 +1,75 @@ +--- +title: Push Notifications +--- + +WalletKit provides the functionality for wallets to receive push notifications through Firebase Cloud Messaging (FCM) and Apple Push Notification Service (APNs) via the Push Server. This feature ensures that wallets are promptly notified of incoming signature requests. Each push notification contains the encrypted details of the signature request. Upon receiving the notification, it can be decrypted and presented to the developer, allowing for customization of the message according to their requirements. + +## Server setup + +For the push notifications to be forwarded to FCM or APNs, the [Push Server](https://docs.reown.com/advanced/push-server) will need to be configured with your FCM or APNs server API credentials. + +## App setup + +### Register the device token + +To enable a device for push notifications, it's essential to register the device token using `WalletKit.registerDeviceToken`. This token can be obtained from either FCM or APNS, depending on the platform used. + +This method enables wallets to receive push notifications from WalletConnect's Push Server via Firebase Cloud Messaging. This means you will have to setup your project with Firebase before being able to call `registerDeviceToken()` method. + +To register a wallet to receive WalletConnect push notifications, call `WalletKit.registerDeviceToken` and pass the Firebase Access Token. + +```kotlin +val firebaseAccessToken: String = //FCM access token received through the Firebase Messaging SDK +val enableEncrypted: Boolean = true //Flag that enables receiveing the detailed notifications + +WalletKit.registerDeviceToken( + firebaseAccessToken = firebaseAccessToken, + enableEncrypted = enableEncrypted, + 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 + }) +``` + +### Receiving push notifications + +After the device token is registered, the next step involves setting up the notification service specific to the platform being used. This service will decrypt the incoming requests and forward them to the developer for further processing and integration. + +The `PushMessagingService` is a wrapper around the `FirebaseMessagingService`. The `PushMessagingService` class needs to be implemented for WalletKit to be able to decrypt and notify wallets of a push notification sent from the Dapp in the background. This service also needs to be registered in the `AndroidManifest.xml` file similar to the example in the FCM documentation. + +```kotlin +class SampleFirebaseService: PushMessagingService() { + + override fun newToken(token: String) { + // Triggered when Firebase Cloud Messaging creates a new token + } + + override fun registeringFailed(token: String, throwable: Throwable) { + // Triggered when Firebase Cloud Messaging if there is an error with registering with the Push Server with a new token + } + + override fun onMessage(message: Core.Model.Message, originalMessage: RemoteMessage) { + // Triggered when a message is sent from the Push Server through Firebase Cloud Messaging and the message contains `Core.Model.Message`. The original FCM RemoteMessage is also returned + } + + override fun onDefaultBehavior(message: RemoteMessage) { + // Triggered when a message is sent from the Push Server through Firebase Cloud Messaging and the message does not contain `Core.Model.Message` in the payload. The original FCM RemoteMessage returned instead + } + + override fun onError(throwable: Throwable, defaultMessage: RemoteMessage) { + // Triggered when there is an error that occurs when a message is received from the Push Server + } +} +``` + +```xml + + + + + + + +``` diff --git a/wallets/android/one-click-auth.mdx b/wallets/android/one-click-auth.mdx new file mode 100644 index 0000000..10b527e --- /dev/null +++ b/wallets/android/one-click-auth.mdx @@ -0,0 +1,114 @@ +--- +title: One-click Auth +--- + +## Introduction + +This section outlines an innovative protocol method that facilitates the initiation of a Sign session and the authentication of a wallet through a [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361) (SIWE) message, enhanced by [ReCaps](https://eips.ethereum.org/EIPS/eip-5573) (ReCap Capabilities). + +This enhancement not only offers immediate authentication for dApps, paving the way for prompt user logins, but also integrates informed consent for authorization. Through this mechanism, dApps can request the delegation of specific capabilities to perform actions on behalf of the wallet user. These capabilities, encapsulated within SIWE messages as ReCap URIs, detail the scope of actions authorized by the user in an explicit and human-readable form. + +By incorporating ReCaps, this method extends the utility of SIWE messages, allowing dApps to combine authentication with a nuanced authorization model. This model specifies the actions a dApp is authorized to execute on the user's behalf, enhancing security and user autonomy by providing clear consent for each delegated capability. As a result, dApps can utilize these consent-backed messages to perform predetermined actions, significantly enriching the interaction between dApps, wallets, and users within the Ethereum ecosystem. + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/authenticatedSessions-light.png) + + +## Handling Authentication Requests + +To handle incoming authentication requests, set up WalletKit.WalletDelegate. The onSessionAuthenticate callback will notify you of any authentication requests that need to be processed, allowing you to either approve or reject them based on your application logic. + +```kotlin +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 +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 + 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 +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. + + diff --git a/wallets/android/resources.mdx b/wallets/android/resources.mdx new file mode 100644 index 0000000..3b1ad7a --- /dev/null +++ b/wallets/android/resources.mdx @@ -0,0 +1,27 @@ +--- +title: 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/)) diff --git a/wallets/android/usage.mdx b/wallets/android/usage.mdx new file mode 100644 index 0000000..a37f38a --- /dev/null +++ b/wallets/android/usage.mdx @@ -0,0 +1,429 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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. + + + +## Initialization + +```kotlin +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 +[ + { + "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 +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 + 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 +{ + //... + 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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 + }) +``` diff --git a/wallets/android/verify.mdx b/wallets/android/verify.mdx new file mode 100644 index 0000000..882a04e --- /dev/null +++ b/wallets/android/verify.mdx @@ -0,0 +1,50 @@ +--- +title: 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 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 +data class VerifyContext( + val id: Long, + val origin: String, + val validation: Model.Validation, + val verifyUrl: String +) + +enum class Validation { + VALID, INVALID, UNKNOWN +} +``` diff --git a/wallets/c-sharp/cloud/analytics.mdx b/wallets/c-sharp/cloud/analytics.mdx new file mode 100644 index 0000000..f78ba0b --- /dev/null +++ b/wallets/c-sharp/cloud/analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +--- + +import Analytics from "/snippets/cloud/analytics.mdx"; + + diff --git a/wallets/c-sharp/cloud/explorer-submission.mdx b/wallets/c-sharp/cloud/explorer-submission.mdx new file mode 100644 index 0000000..e5f11c8 --- /dev/null +++ b/wallets/c-sharp/cloud/explorer-submission.mdx @@ -0,0 +1,7 @@ +--- +title: Explorer Submission +--- + +import ExplorerSubmission from "/snippets/cloud/explorer-submission.mdx"; + + diff --git a/wallets/c-sharp/cloud/relay.mdx b/wallets/c-sharp/cloud/relay.mdx new file mode 100644 index 0000000..5f9e1c0 --- /dev/null +++ b/wallets/c-sharp/cloud/relay.mdx @@ -0,0 +1,7 @@ +--- +title: Relay +--- + +import Relay from "/snippets/cloud/relay.mdx"; + + diff --git a/wallets/c-sharp/installation.mdx b/wallets/c-sharp/installation.mdx new file mode 100644 index 0000000..00e9bdf --- /dev/null +++ b/wallets/c-sharp/installation.mdx @@ -0,0 +1,13 @@ +--- +title: Installation +--- + +Install the Wallet SDK client package via Nuget. + +```bash +dotnet add package Reown.WalletKit +``` + +## Next Steps + +Now that you've installed Wallet SDK, you're ready to start integrating it. The next section will walk you through the process of setting up your project to use the SDK. diff --git a/wallets/c-sharp/usage.mdx b/wallets/c-sharp/usage.mdx new file mode 100644 index 0000000..6de5558 --- /dev/null +++ b/wallets/c-sharp/usage.mdx @@ -0,0 +1,378 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + + +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 + + + +## 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 +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 +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 +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 +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 +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 +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 +{ + //... + 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 +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 +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 +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 +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 +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 +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 +[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 +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 +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 +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 +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 +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 +var sessionTopic = sessionData.Topic; +await walletClient.Disconnect(sessionTopic); + +// or + +await walletClient.Disconnect(sessionTopic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); +``` diff --git a/wallets/c-sharp/verify.mdx b/wallets/c-sharp/verify.mdx new file mode 100644 index 0000000..8aaaf39 --- /dev/null +++ b/wallets/c-sharp/verify.mdx @@ -0,0 +1,99 @@ +--- +title: 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. + +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 +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, +} +``` diff --git a/wallets/chains/adi.mdx b/wallets/chains/adi.mdx new file mode 100644 index 0000000..ce9f88f --- /dev/null +++ b/wallets/chains/adi.mdx @@ -0,0 +1,31 @@ +--- +title: ADI Chain +description: "Overview of ADI Chain integration with Wallet SDK." +--- + +ADI Chain is a fully EVM-compatible blockchain. It uses the standard Ethereum JSON-RPC methods for all wallet interactions. + +## Network / Chain Information + +| CAIP-2 | Chain ID | Name | RPC Endpoint | Explorer | Namespace | +| -------------- | -------- | --------- | ------------------------------- | --------------------------------- | --------- | +| `eip155:36900` | `36900` | ADI Chain | `https://rpc.adifoundation.ai` | `https://explorer.adifoundation.ai` | `eip155` | + +## RPC Methods + +As an EVM-compatible chain, ADI Chain supports all standard Ethereum JSON-RPC methods. Wallets implementing ADI Chain support should refer to the [EVM RPC documentation](/wallets/chains/evm) for the complete list of supported methods, including: + +- `personal_sign` - Sign a message +- `eth_sign` - Sign data +- `eth_signTypedData` / `eth_signTypedData_v4` - Sign typed data (EIP-712) +- `eth_sendTransaction` - Send a transaction +- `eth_signTransaction` - Sign a transaction without broadcasting +- `eth_sendRawTransaction` - Broadcast a signed transaction + +For detailed method specifications and examples, see the [EVM Chain Support](/wallets/chains/evm) page. + +## Additional Resources + +- [ADI Explorer](https://explorer.adifoundation.ai) +- [ADI Bridge](https://bridge.adifoundation.ai) +- [ADI RPC Endpoint](https://rpc.adifoundation.ai) diff --git a/wallets/chains/bitcoin.mdx b/wallets/chains/bitcoin.mdx new file mode 100644 index 0000000..633e7c9 --- /dev/null +++ b/wallets/chains/bitcoin.mdx @@ -0,0 +1,311 @@ +--- +title: Bitcoin +description: "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} +// 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} +// 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} +// 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" + } +} +``` diff --git a/wallets/chains/canton.mdx b/wallets/chains/canton.mdx new file mode 100644 index 0000000..67cf6dc --- /dev/null +++ b/wallets/chains/canton.mdx @@ -0,0 +1,581 @@ +--- +title: Canton +description: "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} +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} +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} +{ + "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} +{ + "id": 1234, + "jsonrpc": "2.0", + "result": { + "status": "executed", + "commandId": "d290f1ee-...", + "payload": { + "updateId": "tx-update-id", + "completionOffset": 42 + } + } +} +``` + +#### Error Response + +```json theme={null} +{ + "id": 1234, + "jsonrpc": "2.0", + "error": { + "code": 5001, + "message": "Transaction execution failed: INVALID_ARGUMENT: ..." + } +} +``` + +#### User Rejected Response + +```json theme={null} +{ + "id": 1234, + "jsonrpc": "2.0", + "error": { + "code": 5000, + "message": "User rejected" + } +} +``` + +--- + +### canton_listAccounts + +Retrieve all configured wallet accounts. + +#### Request + +```json theme={null} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "canton_listAccounts", + "params": {} + } +} +``` + +#### Response + +```json 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} +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} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "canton_getPrimaryAccount", + "params": {} + } +} +``` + +#### Response + +```json 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} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "canton_getActiveNetwork", + "params": {} + } +} +``` + +#### Response + +```json 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} +{ + "topic": "", + "chainId": "canton:devnet", + "request": { + "method": "canton_status", + "params": {} + } +} +``` + +#### Response (ledger reachable) + +```json 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} +{ + "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} +interface CantonLedgerApiRequest { + method: 'canton_ledgerApi'; + params: CantonLedgerApiParams; +} + +interface CantonLedgerApiParams { + requestMethod: 'GET' | 'POST'; + resource: string; + body?: string | object; +} +``` + +#### Example Request + +```json 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} +{ + "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} +interface CantonSignMessageRequest { + method: 'canton_signMessage'; + params: { + message: string; + }; +} +``` + +#### Example Request + +```json 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} +{ + "id": 1240, + "jsonrpc": "2.0", + "result": { + "signature": "", + "publicKey": "" + } +} +``` + +## Events + +### accountsChanged + +Emitted when wallet accounts are added, removed, or modified. + +```json 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} +{ + "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} +{ + "name": "chainChanged", + "data": { + "chainId": "canton:production" + } +} +``` + +## Session Lifecycle + +### Pairing + +The dApp creates a pairing URI and delivers it to the wallet: + +```typescript +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} +{ + "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. diff --git a/wallets/chains/evm.mdx b/wallets/chains/evm.mdx new file mode 100644 index 0000000..334864a --- /dev/null +++ b/wallets/chains/evm.mdx @@ -0,0 +1,338 @@ +--- +title: Ethereum +description: "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} +// 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} +// 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} +[ + "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} +// 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} +[ + { + 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} +// 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} +[ + { + 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} +// 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} +// Request +{ + "id": 1, + "jsonrpc": "2.0", + "method": "eth_sendRawTransaction", + "params":[ + "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f07244567" + ], +} + +// Result +{ + "id": 1, + "jsonrpc": "2.0", + "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" +} +``` diff --git a/wallets/chains/overview.mdx b/wallets/chains/overview.mdx new file mode 100644 index 0000000..f799192 --- /dev/null +++ b/wallets/chains/overview.mdx @@ -0,0 +1,24 @@ +--- +title: Chain Support +sidebarTitle: Overview +--- + +The Wallet SDK is built to be **chain-agnostic** — it supports integrations across multiple blockchain ecosystems by working closely with each chain's foundations and developer communities to standardize namespaces, transaction flows, and JSON-RPC methods. + +## Ecosystem Reference Pages +- [EVM](/wallets/chains/evm) +- [Solana](/wallets/chains/solana) +- [Bitcoin](/wallets/chains/bitcoin) +- [SUI](/wallets/chains/sui) +- [Stacks](/wallets/chains/stacks) +- [TON](/wallets/chains/ton) +- [Tron](/wallets/chains/tron) +- [ADI Chain](/wallets/chains/adi) +- [Canton](/wallets/chains/canton) +- [Stellar](/wallets/chains/stellar) + +## Adding New Chain Support + +Interested in adding support for a new blockchain ecosystem? We work closely with chain foundations and developer communities to standardize integration specifications. + +**Contact us to start the process:** [sales@walletconnect.com](mailto:sales@walletconnect.com) diff --git a/wallets/chains/solana.mdx b/wallets/chains/solana.mdx new file mode 100644 index 0000000..94b37ad --- /dev/null +++ b/wallets/chains/solana.mdx @@ -0,0 +1,272 @@ +--- +title: Solana +description: "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} +// 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} +// 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} +// 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} +// 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} +// 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} +// 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 + } +} +``` diff --git a/wallets/chains/stacks.mdx b/wallets/chains/stacks.mdx new file mode 100644 index 0000000..f91cf2b --- /dev/null +++ b/wallets/chains/stacks.mdx @@ -0,0 +1,263 @@ +--- +title: Stacks +description: "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 +{ + "id": 1, + "jsonrpc": "2.0", + "method": "stx_getAddresses", + "params": {} +} +``` + +#### Response + +```json +{ + "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 +{ + "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 +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "txid": "1234567890abcdef1234567890abcdef12345678", + "transaction": "0x…" + } +} +``` + +### stx_signTransaction + +Sign a Stacks transaction. Optional broadcast. + +#### Request + +```json +{ + "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 +{ + "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 +{ + "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 +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "signature": "0x…" + } +} +``` + +### stx_signStructuredMessage + +Domain-bound structured signing (SIP-018). + +#### Request + +```json +{ + "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 +{ + "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 +{ + "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 +{ + "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. diff --git a/wallets/chains/stellar.mdx b/wallets/chains/stellar.mdx new file mode 100644 index 0000000..4589dc5 --- /dev/null +++ b/wallets/chains/stellar.mdx @@ -0,0 +1,382 @@ +--- +title: Stellar +description: "Overview of the Stellar JSON-RPC methods supported by Wallet SDK." +--- + +These are the methods that wallets should implement to handle Stellar transactions and messages via WalletConnect. + + +The Stellar RPC standard is a proposal still under review and specifications may change. Implementation details and method signatures are subject to updates. + + +## Network / Chain Information + +| Item | Form | +| ------- | -------------------------------------------------------------------------------------------------------- | +| CAIP-2 | `stellar:pubnet` OR `stellar:testnet` | +| CAIP-10 | `stellar:pubnet:G…` — base32 StrKey account ID (56 chars, version byte `0x30`) | +| CAIP-19 | `stellar:pubnet/slip44:148` (XLM) or `stellar:pubnet/asset:{code}-{issuer}` (issued assets) | + +The CAIP-2 reference (`pubnet` / `testnet`) matches the [Stellar CAIP-2 namespace draft](https://github.com/ChainAgnostic/namespaces/blob/main/stellar/caip2.md). It is **not** the network passphrase — that is a separate signing-domain constant wallets bind into every signature, derived from the chain (see [Signing semantics](#signing-semantics)). + +### Account format + +Account IDs returned to the dApp are **CAIP-10 strings**, e.g.: + +```plain +stellar:pubnet:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN +``` + +Wallets MUST return the **G… StrKey** form (Ed25519 public key + CRC16 checksum, base32-encoded). Wallets MUST NOT return: + +- Muxed account (`M…`) IDs — these require a separate spec (CAP-27) and are not universally supported. +- Pre-auth (`T…`) or signer-hash (`X…`) StrKey forms — these are not accounts. +- Raw 32-byte public keys without StrKey encoding. + +### XDR encoding convention + +All transaction payloads cross the wire as **base64-encoded XDR strings**, matching SDF's reference SDKs and Horizon's `/transactions?tx=…` parameter. Specifically: + +- `stellar_signXDR` and `stellar_signAndSubmitXDR` accept and return a base64-encoded **`TransactionEnvelope`** XDR. +- The envelope's discriminant determines tx version: `ENVELOPE_TYPE_TX_V0`, `ENVELOPE_TYPE_TX`, or `ENVELOPE_TYPE_TX_FEE_BUMP`. +- Wallets MUST accept all three; wallets MAY emit signatures only on V1 and fee-bump envelopes (V0 is deprecated). + +## Session Proposal + +A standard WalletConnect session proposal for a Stellar-enabled dApp: + +```json +{ + "optionalNamespaces": { + "stellar": { + "chains": ["stellar:pubnet"], + "methods": [ + "stellar_signXDR", + "stellar_signAndSubmitXDR", + "stellar_signMessage", + "stellar_signAuthEntry" + ], + "events": ["accountsChanged", "chainChanged"] + } + } +} +``` + +| Property | Optional methods | +| ------------------ | --------------------------- | +| Sign transaction | `stellar_signXDR` | +| Sign a message | `stellar_signMessage` | +| Sign and Submit | `stellar_signAndSubmitXDR` | +| Soroban | `stellar_signAuthEntry` | + +## RPC Methods + +### stellar_signXDR + +Asks the wallet to attach a signature to a Stellar `TransactionEnvelope` and return the resulting envelope **without broadcasting it**. The dApp (or a relayer it trusts) is responsible for submission. + +This is the **primary method** for fee-abstracted flows: the dApp constructs an inner transaction whose `source_account` is the buyer; the wallet signs as the buyer; a separate fee-source wraps the result in a `FeeBumpTransactionEnvelope` and submits. + +#### Parameters + +`Object`: + +| Field | Type | Required | Description | +| --------- | ----------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `xdr` | `string` (base64) | yes | The unsigned (or partially-signed) `TransactionEnvelope` XDR to sign. | +| `chain` | `string` (CAIP-2) | yes | Must equal the session's selected chain — `stellar:pubnet`. Wallet MUST reject signing if the encoded `network_id` inside the tx does not match this chain. | +| `account` | `string` (CAIP-10)| yes | The account that should sign. Wallet MUST reject if it doesn't custody this account. | + +This method **signs only**. To sign and broadcast in a single round-trip, use [`stellar_signAndSubmitXDR`](#stellar-signandsubmitxdr). + +#### Returns + +`Object`: + +| Field | Type | Description | +| --------------- | ------------------ | --------------------------------------------------------------------------------- | +| `signedXDR` | `string` (base64) | The signed `TransactionEnvelope` XDR. Existing signatures in the input are preserved. | +| `signerAddress` | `string` (CAIP-10) | The account that signed (echoes `account`). | + +#### Example + +```json +// Request +{ + "id": 1, + "jsonrpc": "2.0", + "method": "stellar_signXDR", + "params": { + "xdr": "AAAAAgAAAACz/ZNn8sJpz0r1A/8mO0wQVjEPFNG8mU3sk1Wk7TPxIQAAAGQAGYGzAAAACgAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAEsbESgFLrQc4j7yo2Up/EWNqQhgvBYGgYNCu9EuRRl+AAAAAVVTREMAAAAA...", + "chain": "stellar:pubnet", + "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" + } +} + +// Response +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "signedXDR": "AAAAAgAAAACz/ZNn8sJpz0r1A/8mO0wQVjEPFNG8mU3sk1Wk7TPxIQAAAGQAGYGzAAAACgAAAAAAAAAAAAAA...AAAAAEFNB7s=", + "signerAddress": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" + } +} +``` + +### stellar_signAndSubmitXDR + +Asks the wallet to sign and submit a transaction in one step. The wallet broadcasts via its configured RPC (Horizon or Stellar RPC) and returns the resulting transaction hash. + +Use this method when the dApp does **not** operate a relayer (i.e. the buyer is paying their own XLM fee directly). + +#### Parameters + +`Object`: + +| Field | Type | Required | Description | +| ------------------ | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| `xdr` | `string` (base64) | yes | The unsigned `TransactionEnvelope` XDR. | +| `chain` | `string` (CAIP-2) | yes | Must equal the session's selected chain. | +| `account` | `string` (CAIP-10) | yes | Signing account. | +| `waitForInclusion` | `boolean` | no | If `true`, wallet waits up to ledger-close time before responding and returns `successful`. If `false` (default), wallet responds as soon as it receives the submission ack from its RPC. | + +#### Returns + +`Object`: + +| Field | Type | Description | +| ------------ | ------------------ | ------------------------------------------------------------------------------------------------ | +| `tx_hash` | `string` (hex, 64) | The hash of the submitted transaction. | +| `signedXDR` | `string` (base64) | The final signed `TransactionEnvelope` XDR (so the dApp can independently verify the hash). | +| `successful` | `boolean` | Optional. Present only when `waitForInclusion: true`. `true` if the tx landed and `successful=true` in its result envelope. | + +#### Example + +```json +// Request +{ + "id": 2, + "jsonrpc": "2.0", + "method": "stellar_signAndSubmitXDR", + "params": { + "xdr": "AAAAAgAAAACz/ZNn8sJpz0r1A/8mO0wQVjEPFNG8mU3sk1Wk7TPxIQAAAGQAGYGzAAAA...", + "chain": "stellar:pubnet", + "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ", + "waitForInclusion": true + } +} + +// Response +{ + "id": 2, + "jsonrpc": "2.0", + "result": { + "tx_hash": "3389e9f0f1a54f04a78fd09a7e0fc0d44f1eecbe8c33a3d56a39c8b46d2a8b48", + "signedXDR": "AAAAAgAAAACz/ZNn8sJpz0r1...AAAAAEFNB7s=", + "successful": true + } +} +``` + +### stellar_signMessage + +Asks the wallet to sign an arbitrary message under a Stellar account's Ed25519 key, **outside** the context of a Stellar transaction. This enables [SEP-10 web auth](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md)-style sign-in flows and dApp session attestation. + +To prevent a malicious dApp from getting a wallet to sign a payload that is also a valid transaction body, wallets MUST prepend a domain-separating prefix before signing: + +```plain +sign(Ed25519, sha256("Stellar Signed Message:\n" || message)) +``` + +The fixed UTF-8 prefix `"Stellar Signed Message:\n"` (with the trailing newline) is concatenated **directly** with the message bytes — no `0x00` separator and no length prefix — then SHA-256 hashed and Ed25519-signed. This matches the finalized [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md) convention (the same domain-separation approach as Bitcoin/Ethereum signed messages) and ensures cross-context replay is impossible — a SEP-10 challenge transaction would never collide with a `stellar_signMessage` payload. + +#### Parameters + +`Object`: + +| Field | Type | Required | Description | +| ----------------- | ----------------------------- | -------- | --------------------------------------------------------------------------------------------- | +| `message` | `string` (utf-8 OR base64) | yes | The payload to sign. If `messageEncoding: "base64"`, decoded as raw bytes; default `"utf-8"`. | +| `messageEncoding` | `"utf-8"` \| `"base64"` | no | Defaults to `"utf-8"`. | +| `chain` | `string` (CAIP-2) | yes | `stellar:pubnet` (signature is network-agnostic, but the session context is). | +| `account` | `string` (CAIP-10) | yes | Signing account. | + +#### Returns + +`Object`: + +| Field | Type | Description | +| --------------- | ------------------ | ---------------------------------------------- | +| `signature` | `string` (base64) | 64-byte Ed25519 signature, base64-encoded. | +| `signerAddress` | `string` (CAIP-10) | Signing account (echoes input). | + +#### Example + +```json +// Request +{ + "id": 3, + "jsonrpc": "2.0", + "method": "stellar_signMessage", + "params": { + "message": "pay-core://sign-in?nonce=8c4f1a2b&exp=1747746000", + "messageEncoding": "utf-8", + "chain": "stellar:pubnet", + "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" + } +} + +// Response +{ + "id": 3, + "jsonrpc": "2.0", + "result": { + "signature": "iJ7rH9N2T5q3Vh8U8a3l1eC9bD0fK6mPq9R5/4tZv1c9Ek2y0sJgPpVxT8aBhYf3LqW1uAYR7s2qNlDe6cZyAA==", + "signerAddress": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" + } +} +``` + + +**Anti-pattern:** Do NOT sign raw bytes without the domain prefix. Wallets that do so MUST be considered non-compliant — they expose users to transaction-impersonation attacks. + + +### stellar_signAuthEntry (Soroban) + +Signs a Soroban `SorobanAuthorizationEntry`, enabling Soroban contract authorizations to be co-signed by an address that is **not** the transaction's source account. This is the Stellar analogue to Ethereum's EIP-712 typed-data signing for permits / meta-transactions: a user authorizes a specific contract invocation tree, a separate party submits the transaction that consumes the authorization. + +The signing payload is the `HashIDPreimage::SOROBAN_AUTHORIZATION` preimage, computed as: + +```plain +sign(Ed25519, sha256(xdr(HashIDPreimageSorobanAuthorization { + network_id, + nonce, + signature_expiration_ledger, + invocation, +}))) +``` + +All four fields are pulled from the `SorobanCredentials::SOROBAN_CREDENTIALS_ADDRESS` block inside the auth entry. `network_id` is bound by the wallet from the session's CAIP-2 chain (NOT trusted from the request). + +#### Parameters + +`Object`: + +| Field | Type | Required | Description | +| ----------- | ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `authEntry` | `string` (base64) | yes | The unsigned `SorobanAuthorizationEntry` XDR. Its `credentials` MUST be of type `SOROBAN_CREDENTIALS_ADDRESS` with an empty `signature` SCVal. `SOROBAN_CREDENTIALS_SOURCE_ACCOUNT` entries are not signable via this method — they are authorized implicitly by signing the enclosing transaction. | +| `chain` | `string` (CAIP-2) | yes | Must equal the session's selected chain. | +| `account` | `string` (CAIP-10) | yes | The account that should sign. Wallet MUST verify it matches `credentials.address` inside the entry; reject with `4302` otherwise. | + +The wallet MUST also reject (`4304` — `AUTH_EXPIRED`) if `signature_expiration_ledger` is ≤ the current ledger sequence as known to the wallet, with a small safety margin to account for propagation. + +#### Returns + +`Object`: + +| Field | Type | Description | +| ----------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------- | +| `signedAuthEntry` | `string` (base64) | The updated `SorobanAuthorizationEntry` XDR with the `signature` SCVal populated. Other fields (nonce, expiration, invocation) MUST be byte-identical to the input. | +| `signerAddress` | `string` (CAIP-10) | Echoes `account`. | + +The `signature` SCVal follows Stellar's account-contract signer convention: an `SCMap` with keys `"public_key"` (32-byte Ed25519 pubkey as `SCBytes`) and `"signature"` (64-byte Ed25519 signature as `SCBytes`). Wallets MUST NOT emit a raw 64-byte signature without the map wrapper — Soroban host code rejects it. + +#### Example + +```json +// Request — dApp wants the user to authorize a transfer(from=user, to=merchant, amount=10_0000000) invocation +{ + "id": 4, + "jsonrpc": "2.0", + "method": "stellar_signAuthEntry", + "params": { + "authEntry": "AAAAAQAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAAAAAAAAQAGgaUAAAAAAAAAAa1uVUtkbThwc1ZmTGdEYzVlbnVsbAAAAAAAAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAASAAAAAAAAAABLGxEoBS60HOI+8qNlKfxFjakIYLwWBoGDQrvRLkUZfgAAAAoAAAAAAAAAAAAAAAAF9eEAAAAAAAAAAAA=", + "chain": "stellar:pubnet", + "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" + } +} + +// Response — same entry, signature SCVal now populated with the account-contract signer map +{ + "id": 4, + "jsonrpc": "2.0", + "result": { + "signedAuthEntry": "AAAAAQAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAAAAAAAAQAGgaUAAAAAAAAAAa1uVUtkbThwc1ZmTGdEYzVlbnVsbAAAAAAAAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAASAAAAAAAAAABLGxEoBS60HOI+8qNlKfxFjakIYLwWBoGDQrvRLkUZfgAAAAoAAAAAAAAAAAAAAAAF9eEAAAARAAAAAQAAAAIAAAAPAAAACnB1YmxpY19rZXkAAAAAAA0AAAAgs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAAPAAAACXNpZ25hdHVyZQAAAAAAAA0AAABAi3hvR9N+a5q1Vh8U8a3l1eC9bD0fK6mPq9R5/4tZv1c9Ek2y0sJgPpVxT8aBhYf3LqW1uAYR7s2qNlDe6cZyAA==", + "signerAddress": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" + } +} +``` + +## Events + +| Event | Payload | Notes | +| ----------------- | ------------------------------------ | -------------------------------------------------------------------------------------------- | +| `accountsChanged` | `{ accounts: string[] }` (CAIP-10) | Emitted when the user changes the active account in their wallet, or revokes access for an account. | +| `chainChanged` | `{ chainId: string }` (CAIP-2) | Emitted when the wallet switches the active network (e.g. `stellar:pubnet` ↔ `stellar:testnet`). | + +## Signing semantics + +### Network passphrase binding + +Every Stellar transaction signature is computed over: + +```plain +sign(Ed25519, sha256(network_id || envelope_payload)) +``` + +where `network_id = sha256("Public Global Stellar Network ; September 2015")` for pubnet. This is **inside** the XDR envelope and is the protocol-level replay protection across networks. Wallets MUST: + +1. Decode the envelope's signing payload, **not** the wire bytes, before signing. +2. Compute `network_id` from the network the session belongs to (`stellar:pubnet` → pubnet passphrase). Wallets MUST NOT trust a `network_id` embedded in the request — only the CAIP-2 chain identifier. +3. Refuse to sign if the decoded envelope's internal network reference (when present, e.g. on fee-bump inner txs) does not match the session chain. + +### Fee-bump envelopes + +When the dApp passes a `FeeBumpTransactionEnvelope` to `stellar_signXDR`: + +- The wallet signs **only the inner tx**, not the outer fee-bump envelope. The outer envelope is signed by the `fee_source` account (typically a different party — the relayer). +- Wallets MUST validate that the inner tx's `source_account` is in fact the `account` parameter. +- Wallets MAY warn the user that fees are being paid by a different account (`fee_source`), and SHOULD display both the inner source and outer fee source in the signing UI. + +### Computing the tx hash and explorer discoverability + +The transaction hash is **deterministic from the signed envelope** — signatures are computed over the hash, they are not part of it. As soon as a dApp receives `signedXDR` from `stellar_signXDR`, it can derive the same hash the network will use. + +```plain +tx_hash = sha256( network_id ‖ ENVELOPE_TYPE ‖ tx_payload_xdr ) +``` + +| Component | Value | +| ---------------- | ---------------------------------------------------------------------------------------------------------------- | +| `network_id` | `sha256("Public Global Stellar Network ; September 2015")` — 32 bytes, fixed for pubnet | +| `ENVELOPE_TYPE` | XDR-encoded `EnvelopeType` enum (4 bytes, big-endian): `2` (`ENVELOPE_TYPE_TX`) for normal txs, `5` (`ENVELOPE_TYPE_TX_FEE_BUMP`) for fee-bump txs | +| `tx_payload_xdr` | XDR-encoded **`Transaction`** struct (the inner body — NOT the full envelope with its signatures) | + +Adding, removing, or reordering signatures does NOT change the hash. + +Reference computation: + +```typescript +import { TransactionBuilder, Networks } from "@stellar/stellar-sdk"; + +const { signedXDR } = await session.request({ + topic, + chainId: "stellar:pubnet", + request: { method: "stellar_signXDR", params: { xdr, chain, account } }, +}); + +const tx = TransactionBuilder.fromXDR(signedXDR, Networks.PUBLIC); +const txHash = tx.hash().toString("hex"); // 64-char hex +``` + +**Inner hash vs. fee-bump hash.** When `stellar_signXDR` is used inside a fee-abstraction flow (the wallet signs an inner tx, a relayer wraps it in a `FeeBumpTransaction` before submitting), the hash the dApp computes from the inner tx (`H_inner`) is **not** the hash that lands on-chain (`H_fb`). Horizon resolves **either hash** to the same transaction record — a `GET /transactions/{hash}` request works whether you pass `H_inner` or `H_fb`, and both are exposed on the returned fee-bump record. For stable UX, prefer `H_fb` for explorer links once submission is confirmed; `H_inner` works as an immediate optimistic identifier between sign-time and submission. Note that Horizon will **404** on an `H_inner` lookup until the wrapping fee-bump transaction has been submitted and included in a ledger. + +## Additional Resources + +- [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) and [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) — chain and account identifiers. +- [Stellar CAIP-2 namespace draft](https://github.com/ChainAgnostic/namespaces/blob/main/stellar/caip2.md). +- [Stellar SEP-7](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0007.md), [SEP-10](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md), [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md). +- [Freighter — WalletConnect (mobile)](https://docs.freighter.app/mobile-walletconnect/mobile). +- [Stellar XDR reference](https://developers.stellar.org/docs/encyclopedia/xdr). +- [Fee-bump transactions](https://developers.stellar.org/docs/learn/encyclopedia/transactions-specialized/fee-bump-transactions). diff --git a/wallets/chains/sui.mdx b/wallets/chains/sui.mdx new file mode 100644 index 0000000..0930de3 --- /dev/null +++ b/wallets/chains/sui.mdx @@ -0,0 +1,173 @@ +--- +title: Sui +description: "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 +// 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 +// 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 +// 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 +// 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). diff --git a/wallets/chains/ton.mdx b/wallets/chains/ton.mdx new file mode 100644 index 0000000..a588eed --- /dev/null +++ b/wallets/chains/ton.mdx @@ -0,0 +1,208 @@ +--- +title: TON +description: "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} +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} +{ + "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} +{ + "jsonrpc": "2.0", + "id": 123, + "result": "base64bocEncodedTransaction" +} +``` + +### Error Response + +```json 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} +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} +{ + "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} +{ + "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} +{ + "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 +// 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. diff --git a/wallets/chains/tron.mdx b/wallets/chains/tron.mdx new file mode 100644 index 0000000..03f119a --- /dev/null +++ b/wallets/chains/tron.mdx @@ -0,0 +1,263 @@ +--- +title: Tron +description: "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 +{ + "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 +{ + "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 +{ + "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 +{ + "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 +{ "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 +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tron_sendTransaction", + "params": { + "signedTransaction": { + "txID": "66e79c6993f29b02725da54ab146ffb0453ee6a43b4083568ad9585da305374a", + "signature": [ + "7e760cef94bc82a7533bc1e8d4ab88508c6e13224cd50cc8da62d3f4d4e19b99514f..." + ], + "raw_data_hex": "0a02885b2208baa1c278fd0a309f4090c1dbe5e7325aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a15411cb0b7348eded93b8d0816bbeb819fc1d7a51f31121541a614f803b6fd780986a42c78ec9c7f77e6ded13c2244095ea7b30000000000000000000000001cb0b7348eded93b8d0816bbeb819fc1d7a51f3100000000000000000000000000000000000000000000000000000000000000007082f4d7e5e73290018084af5f" + } + } +} +``` + +- Response: + +```json +{ + "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 +{ + "jsonrpc": "2.0", + "id": 1, + "method": "tron_getBalance", + "params": { + "address": "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH" + } +} +``` + +- Response: + +```json +{ + "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/). diff --git a/wallets/custodians/app-access-control.mdx b/wallets/custodians/app-access-control.mdx new file mode 100644 index 0000000..21bc991 --- /dev/null +++ b/wallets/custodians/app-access-control.mdx @@ -0,0 +1,46 @@ +--- +title: How to Control Which Apps Can Connect to Your Users’ Wallets +sidebarTitle: Managing Dapp Access +--- + +As a wallet provider or a custodian, you may want to restrict access to certain dapps to maintain control over which applications can connect to your users’ wallets. This can be useful for a variety of reasons, such as to prevent users from using certain apps that are not compliant with your policies or to simply block certain apps from connecting to your users' wallets. + +Using the Wallet SDK, you can block apps from connecting to your users' wallets by rejecting session requests from certain apps. + +## Prerequisites + +- Please ensure you have integrated Wallet SDK into your wallet. +- Please ensure that you have obtained and configured the project ID from the [WalletConnect Dashboard](https://dashboard.walletconnect.com). + +## Maintaining a Blocklist of Apps + +Wallet SDK allows you to identify malicious apps using [Verify API](/wallets/features/verify). However, as a wallet, you will need to build your own logic for the UI and UX of blocking certain apps. + +If there are specific apps that you want to block (not flagged as malicious by Verify API), you will need to maintain a blocklist of apps by storing the app's metadata in a database or a file. + +## Inspecting Session Requests + +When receiving `onSessionProposal` events, check the dapp's metadata (name, URL, description) from `proposal.proposer.metadata`. + +After this, you can reject unwanted connections by calling `rejectSession()` for apps you want to block. For example: + +```javascript +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. diff --git a/wallets/custodians/app-extended-sessions.mdx b/wallets/custodians/app-extended-sessions.mdx new file mode 100644 index 0000000..eeeb9c5 --- /dev/null +++ b/wallets/custodians/app-extended-sessions.mdx @@ -0,0 +1,77 @@ +--- +title: Extended WalletConnect Sessions Request for Apps +--- + +This guide will walk you through how an app 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 apps need to do? + +An app must: + +- Keep track of outstanding requests and handle delayed responses. +- Have a UI that tolerates waiting for a response as long as the expiry is configured. + +Extended durations may not be suitable for **time-bounded actions** (e.g., swaps with quotes, flash opportunities) where blockchain state changes invalidate the request. + +Hence an app must align **WalletConnect expiry** with any **contract-level expiry** to avoid mismatched timing. + +### Limits + +- **Minimum:** 300 seconds (5 min) +- **Maximum:** 604,800 seconds (7 days) + +## How can I extend the session request expiry as an app? + +Developers can define how long a WalletConnect session request remains active by using the **`expiry`** parameter in the session request payload. + +### Parameter + +`expiry`: Optional field to set session expiry time. Must be in seconds. + +### Implementation + +Below are the examples of how to implement the `expiry` parameter in the session request payload for different providers. + + + ```javascript Sign Client + const result = await signClient.request({ + topic: 'xyz', + request: { + method: 'personal_sign', + params: [ + '0xdeadbeef', + '0x000000000000000000000000000000000000dead' + ] + }, + chainId: 'eip155:1', + expiry: 600 // <-- add this for 10 minutes expiry in seconds + }); + ``` + ```javascript Universal Provider + const result = await universalProvider.request( + { + method: "personal_sign", + params: ["0xdeadbeef", "0x000000000000000000000000000000000000dead"], + }, + "eip155:1", + 600, // <-- add this for 10 minutes expiry in seconds + ); + ``` + ```javascript Ethereum Provider + await ethereumProvider.request( + { + method: "personal_sign", + params: ["0xdeadbeef", "0x000000000000000000000000000000000000dead"], + }, + 600, // <-- add this for 10 minutes expiry in seconds + ); + ``` + + +**Apps should store pending request IDs in local storage to handle delayed responses, app reload or crash scenarios.** \ No newline at end of file diff --git a/wallets/custodians/contract-access-control.mdx b/wallets/custodians/contract-access-control.mdx new file mode 100644 index 0000000..fe0fcc4 --- /dev/null +++ b/wallets/custodians/contract-access-control.mdx @@ -0,0 +1,130 @@ +--- +title: How to Control Which Smart Contracts Your Users Can Interact With +sidebarTitle: Managing Smart Contract Access +--- + +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 +{ + "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 +{ + "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 +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. diff --git a/wallets/custodians/extended-sessions.mdx b/wallets/custodians/extended-sessions.mdx new file mode 100644 index 0000000..4699478 --- /dev/null +++ b/wallets/custodians/extended-sessions.mdx @@ -0,0 +1,36 @@ +--- +title: Extended WalletConnect Sessions Request +sidebarTitle: Extended Sessions Request +--- + +This guide will walk you through how a wallet developer can customize the session request expiry in WalletConnect using the **`expiry`** parameter. + +## Extended Session Expiry - what does it mean? + +When an app sends a request through WalletConnect (for example, a signature request), it stays “active” until the expiry time is reached. If the wallet does not respond before the expiry, the request automatically fails with a timeout. + +By default, the expiry is short, i.e., 5 minutes. Extending the session expiry time allows the wallet and app to keep the request open longer, **up to 7 days**. This is useful for cases like off-hours approvals, delayed custody flows, or multi-party signing. + +### What do wallets need to do? + +A wallet must: +- Maintain pending state until expiry or completion. +- Gracefully discard expired requests. +- Verify user intent remains valid after long delays. + +### Limits + +- **Minimum:** 300 seconds (5 min) +- **Maximum:** 604,800 seconds (7 days) + +## How can I extend the session request expiry as a wallet? + +Wallets must correctly interpret and enforce the expiry. + +- Parse `expiry` in seconds from incoming request metadata. +- Keep pending requests active until they’re resolved or the expiry time elapses. +- Notify the user of pending and expired requests. +- If the expiry has passed, return an error response (`code: 4100`, “Request expired”). +- Optional UX: display countdown timers or “expires in X hours”. + +Please refer to the [Best Practices](/wallets/more/best-practices#session-request-expiry) section to learn how you can implement this in your code. \ No newline at end of file diff --git a/wallets/custodians/overview.mdx b/wallets/custodians/overview.mdx new file mode 100644 index 0000000..9cf24f9 --- /dev/null +++ b/wallets/custodians/overview.mdx @@ -0,0 +1,34 @@ +--- +title: WalletConnect for Custodians and Institutions +sidebarTitle: Quickstart +--- + +**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/). \ No newline at end of file diff --git a/wallets/features/chain-abstraction.mdx b/wallets/features/chain-abstraction.mdx new file mode 100644 index 0000000..3ae78a9 --- /dev/null +++ b/wallets/features/chain-abstraction.mdx @@ -0,0 +1,87 @@ +--- +title: Chain Abstraction +--- +Chain Abstraction allows users to spend stablecoins across different networks seamlessly. +This solution provides wallet developers with a toolkit to integrate cross-chain functionality using WalletKit. + + +💡 Support for Chain Abstraction is currently in early access phase. + + + + +## How it works + +When an application sends a `wallet_sendTransaction` request for an ERC-20 transfer (such as USDC), +the wallet checks for available tokens across all supported networks. If the user has sufficient funds on any supported network, +they can complete the transaction instantly, regardless of which network holds their tokens. + +For example, consider a scenario where an app requests a transfer of 225 USDC on the Base Network. +Even if the user doesn't have USDC on Base, their wallet can automatically source the funds from other networks, +making the experience seamless for both the user and the application. + + +💡 Make sure you have enough gas fees in other networks from which bridging will happen. +For example, in given scenario, you need to have enough gas fees on OP Mainnet and Arbitrum. + + +The diagram below shows an example scenario where a user is interacting with an app and is asked to transfer 225 USDC to the app on Base Network. +The user does not have any USDC on Base, but their wallet seamlessly allows them to source the funds from other networks. + +![Chain Abstraction Example](https://mintcdn.com/reown-5552f0bb/27-yRRcu0Ky--oPV/images/assets/chain_abstraction_demo.png?w=1100&fit=max&auto=format&n=27-yRRcu0Ky--oPV&q=85&s=53d32b39551a6a8be0a3569d3bae1c5a) + +## 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. + + + + +## FAQ + +### What are the available networks for Chain Abstraction? + +Chain Abstraction is available on the following networks: + +- Base +- Arbitrum +- OP Mainnet +- Solana + +### What are the supported tokens and networks? + +Chain Abstraction supports the following tokens across different networks: + +| Network | Assets | +|-----------|-----------------| +| Optimism | USDC, USDT, ETH | +| Arbitrum | USDC, USDT, ETH | +| Base | USDC, USDS, ETH | +| Solana | USDC | + + +### What are the limitations? + +We currently support 1:1 transfers i.e. sourcing funds from one address to another. Make sure that you're transferring minimum 0.6$ worth of tokens and have enough gas to pay bridging fees. diff --git a/wallets/features/link-mode.mdx b/wallets/features/link-mode.mdx new file mode 100644 index 0000000..9cbbe8b --- /dev/null +++ b/wallets/features/link-mode.mdx @@ -0,0 +1,39 @@ +--- +title: 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. + + diff --git a/wallets/features/notifications.mdx b/wallets/features/notifications.mdx new file mode 100644 index 0000000..7976f59 --- /dev/null +++ b/wallets/features/notifications.mdx @@ -0,0 +1,22 @@ +--- +title: Notifications +--- + +Enrich your wallet experience with Web3 Notifications and provide your community with direct access to critical and powerful updates from their favorite apps. +Build in-wallet notification features that allow users to subscribe, set permissions, and receive notifications from their favorite apps. + +## Get Started + + + + Get started with WalletKit in Android. + + + + Get started with WalletKit in iOS. + + + + Get started with WalletKit in React Native. + + diff --git a/wallets/features/one-click-auth.mdx b/wallets/features/one-click-auth.mdx new file mode 100644 index 0000000..a2399a9 --- /dev/null +++ b/wallets/features/one-click-auth.mdx @@ -0,0 +1,31 @@ +--- +title: 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. + + + diff --git a/wallets/features/verify.mdx b/wallets/features/verify.mdx new file mode 100644 index 0000000..dc88da4 --- /dev/null +++ b/wallets/features/verify.mdx @@ -0,0 +1,56 @@ +--- +title: Verify API +sidebarTitle: 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. + + + + + +## Security providers + +Verify API combines WalletConnect's domain registry with threat intelligence from leading web3 security providers. If any of them flags the domain behind a session proposal or session request as malicious, the `verifyContext` of that request is returned with `isScam` set to `true`. + + + + Real-time detection of malicious contracts, transactions and web3 threats. + + + + Crypto-native anti-phishing intelligence, including scam domain detection and takedowns. + + + + Real-time monitoring of malicious dapps, wallet drainers and phishing domains. + + + +## 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. + + diff --git a/wallets/flutter/chain-abstraction.mdx b/wallets/flutter/chain-abstraction.mdx new file mode 100644 index 0000000..a75f348 --- /dev/null +++ b/wallets/flutter/chain-abstraction.mdx @@ -0,0 +1,244 @@ +--- +title: Chain Abstraction +--- + +import HowItWorks from "/snippets/walletkit/shared/chain-abstraction/intro.mdx"; +import ErrorHandling from "/snippets/walletkit/shared/chain-abstraction/error-handling.mdx"; + + + +## 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 +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 +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 +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 +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 +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.** + ``` + + + +## 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](/wallets/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction-supported wallet. + + diff --git a/wallets/flutter/cloud/analytics.mdx b/wallets/flutter/cloud/analytics.mdx new file mode 100644 index 0000000..f78ba0b --- /dev/null +++ b/wallets/flutter/cloud/analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +--- + +import Analytics from "/snippets/cloud/analytics.mdx"; + + diff --git a/wallets/flutter/cloud/explorer-submission.mdx b/wallets/flutter/cloud/explorer-submission.mdx new file mode 100644 index 0000000..e5f11c8 --- /dev/null +++ b/wallets/flutter/cloud/explorer-submission.mdx @@ -0,0 +1,7 @@ +--- +title: Explorer Submission +--- + +import ExplorerSubmission from "/snippets/cloud/explorer-submission.mdx"; + + diff --git a/wallets/flutter/cloud/relay.mdx b/wallets/flutter/cloud/relay.mdx new file mode 100644 index 0000000..5f9e1c0 --- /dev/null +++ b/wallets/flutter/cloud/relay.mdx @@ -0,0 +1,7 @@ +--- +title: Relay +--- + +import Relay from "/snippets/cloud/relay.mdx"; + + diff --git a/wallets/flutter/eip5792.mdx b/wallets/flutter/eip5792.mdx new file mode 100644 index 0000000..cdc8e64 --- /dev/null +++ b/wallets/flutter/eip5792.mdx @@ -0,0 +1,284 @@ +--- +title: Wallet Call API +--- + +WalletConnect supports [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability), which defines new JSON-RPC methods that enable apps to ask a wallet to process a batch of onchain write calls and to check on the status of those calls. +Applications can specify that these onchain calls be executed taking advantage of specific capabilities previously expressed by the wallet; an additional, a novel wallet RPC is defined to enable apps to query the wallet for those capabilities. + +- `wallet_sendCalls`: Requests that a wallet submits a batch of calls. +- `wallet_getCallsStatus`: Returns the status of a call batch that was sent via wallet_sendCalls. +- `wallet_showCallsStatus`: Requests that a wallet shows information about a given call bundle that was sent with wallet_sendCalls. +- `wallet_getCapabilities`: This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication). + +## Usage + + + + ## 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 +"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 +"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 +"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 + { + "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 + { + "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 + { + "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 + { + "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 + { + "id": "...", + "capabilities": { + "caip345": { + "caip2": "eip155:1", + "transactionHashes": ["..."], + } + } + } + ``` + + Specify the `scopedProperties` when approving a session: + + ```json + "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 + { + "receipts": [/* single receipt or array of receipts */], + "atomic": true + } + ``` + + #### For Non-Atomic Execution + ```json + { + "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 +- CAIP-25 namespaces: https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md diff --git a/wallets/flutter/installation.mdx b/wallets/flutter/installation.mdx new file mode 100644 index 0000000..4b2f6a5 --- /dev/null +++ b/wallets/flutter/installation.mdx @@ -0,0 +1,33 @@ +--- +title: 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 +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. diff --git a/wallets/flutter/link-mode.mdx b/wallets/flutter/link-mode.mdx new file mode 100644 index 0000000..9901e14 --- /dev/null +++ b/wallets/flutter/link-mode.mdx @@ -0,0 +1,106 @@ +--- +title: Link Mode +--- + +WalletKit Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallets/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](/wallets/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} +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 +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 + + + + + com.apple.developer.associated-domains + + applinks: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 + + + + + + + + + + +``` + +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. + +
+
diff --git a/wallets/flutter/mobile-linking.mdx b/wallets/flutter/mobile-linking.mdx new file mode 100644 index 0000000..686c7c8 --- /dev/null +++ b/wallets/flutter/mobile-linking.mdx @@ -0,0 +1,232 @@ +--- +title: Mobile Linking +--- + +import HowToTest from "/snippets/walletkit/shared/mobile-linking.mdx"; + + + + +This feature is only relevant to native platforms. + + + +## Usage + +Mobile Linking allows your wallet to automatically redirect back to the Dapp allowing for less user interactions and hence a better UX for your users. + +### Establishing Communication Between Mobile Wallets and Apps + +When integrating a wallet with a mobile application, it's essential to understand how they communicate. The process involves two main steps: + +1. **QR Code Handshake:** The mobile app (Dapp) generates a unique URI (Uniform Resource Identifier) and displays it as a QR code. This URI acts like a secret handshake. When the user scans the QR code using their wallet app, they establish a connection. It's like saying, "Hey, let's chat!" +2. **Deep Links and Universal Links:** The URI from the QR code allows the wallet app to create a [deep link](https://support.google.com/google-ads/answer/10023042?hl=en#:~:text=Deep%20links%20send%20mobile%20device,%2C%20Shopping%2C%20and%20Display%20campaigns.) or [universal link](https://developer.apple.com/ios/universal-links/). These links work on both Android and iOS. They enable seamless communication between the wallet and the app. + + + +**Developers should prefer Deep Linking over Universal Linking.** + +Universal Linking may redirect the user to a browser, which might not provide the intended user experience. Deep Linking ensures the user is taken directly to the app. + + + +### Key Behavior to Address + +In some scenarios, wallets use redirect metadata provided in session proposals to open applications. This can cause unintended behavior, such as: + +Redirecting to the wrong app when multiple apps share the same redirect metadata (e.g., a desktop and mobile version of the same Dapp). +Opening an unrelated application if a QR code is scanned on a different device than where the wallet is installed. + +#### Recommended Approach + +To avoid this behavior, wallets should: + +- **Restrict Redirect Metadata to Deep Link Use Cases**: Redirect metadata should only be used when the session proposal is initiated through a deep link. QR code scans should not trigger app redirects using session proposal metadata. + +The connection and sign request flows are similar across platforms. + +### Connection Flow + +- **Dapp Prompts User:** The Dapp asks the user to connect. +- **User Chooses Wallet:** The user selects a wallet from a list of compatible wallets. +- **Redirect to Wallet:** The user is redirected to their chosen wallet. +- **Wallet Approval:** The wallet prompts the user to approve or reject the session (similar to granting permission). +- **Return to Dapp:** + - **Manual Return:** The wallet asks the user to manually return to the Dapp. + - **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. +- **User Reunites with Dapp:** After all the interactions, the user ends up back in the Dapp. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking-light.png) + + +### Sign Request Flow + +When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs: + +- **Automatic Redirect:** The Dapp automatically sends the user to their previously chosen wallet. +- **Approval Prompt:** The wallet asks the user to approve or reject the request. +- **Return to Dapp:** + - **Manual Return:** The wallet asks the user to manually return to the Dapp. + - **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. +- **User Reconnects:** Eventually, the user returns to the Dapp. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking_sign-light.png) + + +## Platform preparations + + + + +In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to add your 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 +CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleURLSchemes + + examplewallet + + + +``` + + + +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 + + + + + + +``` + + + + +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 +FlutterDeepLinkingEnabled + +``` + +More information in official documentation: 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 + + + + + +## 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 +Future redirectToDapp({ + required String topic, + required Redirect? redirect, +}) +``` + +After Session Proposal: + +```javascript +_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 +// 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 +// 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. + + diff --git a/wallets/flutter/one-click-auth.mdx b/wallets/flutter/one-click-auth.mdx new file mode 100644 index 0000000..40655f2 --- /dev/null +++ b/wallets/flutter/one-click-auth.mdx @@ -0,0 +1,146 @@ +--- +title: One-click Auth +--- + +## Introduction + +This section outlines an innovative protocol method that facilitates the initiation of a Sign session and the authentication of a wallet through a [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361) (SIWE) message, enhanced by [ReCaps](https://eips.ethereum.org/EIPS/eip-5573) (ReCap Capabilities). + +This enhancement not only offers immediate authentication for dApps, paving the way for prompt user logins, but also integrates informed consent for authorization. Through this mechanism, dApps can request the delegation of specific capabilities to perform actions on behalf of the wallet user. These capabilities, encapsulated within SIWE messages as ReCap URIs, detail the scope of actions authorized by the user in an explicit and human-readable form. + +By incorporating ReCaps, this method extends the utility of SIWE messages, allowing dApps to combine authentication with a nuanced authorization model. This model specifies the actions a dApp is authorized to execute on the user's behalf, enhancing security and user autonomy by providing clear consent for each delegated capability. As a result, dApps can utilize these consent-backed messages to perform predetermined actions, significantly enriching the interaction between dApps, wallets, and users within the Ethereum ecosystem. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/authenticatedSessions-light.png) + + +## Handling Authentication Requests + +To handle incoming authentication requests, 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 +// 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 +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 +// 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 +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. + + diff --git a/wallets/flutter/usage.mdx b/wallets/flutter/usage.mdx new file mode 100644 index 0000000..7587fe4 --- /dev/null +++ b/wallets/flutter/usage.mdx @@ -0,0 +1,448 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 + + + +## Initialization + +To create an instance of ReownWalletKit, you need to pass in the `core` and `metadata` parameters. + +```javascript +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 +// 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 +{ + //... + 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 +_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 +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 +_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 +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 +_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 +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 +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 +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 +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 +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 +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 +// 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) diff --git a/wallets/flutter/verify.mdx b/wallets/flutter/verify.mdx new file mode 100644 index 0000000..ca3b621 --- /dev/null +++ b/wallets/flutter/verify.mdx @@ -0,0 +1,46 @@ +--- +title: 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. + +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 +_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; + } +}); +``` \ No newline at end of file diff --git a/wallets/guides/tonconnect-walletconnect.mdx b/wallets/guides/tonconnect-walletconnect.mdx new file mode 100644 index 0000000..ad901be --- /dev/null +++ b/wallets/guides/tonconnect-walletconnect.mdx @@ -0,0 +1,179 @@ +--- +title: TON Connect WalletConnect Integration +sidebarTitle: TON Connect + WalletConnect +description: "Learn how to enable WalletConnect support in your TON Connect application." +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 and obtain a new project ID. You will need this project ID to initialize WalletConnect in your application. + + + +### 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 +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 +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 +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 +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](/wallets/chains/ton) documentation. diff --git a/wallets/ios/best-practices.mdx b/wallets/ios/best-practices.mdx new file mode 100644 index 0000000..a75a736 --- /dev/null +++ b/wallets/ios/best-practices.mdx @@ -0,0 +1,213 @@ +--- +title: 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 +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 +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 +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 +do { + try await WalletKit.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties) + // Update UI, remove loader +} catch { + // present error +} +``` + +Session Reject + +```swift +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 +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 +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 +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 +WalletKit.instance.socketConnectionStatusPublisher + .receive(on: DispatchQueue.main) + .sink { status in + switch status { + case .connected: + // ... + case .disconnected: + // ... + } +}.store(in: &publishers) +``` + +### Expected User flow + +### Connection State + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/connection_state.gif) + \ No newline at end of file diff --git a/wallets/ios/chain-abstraction.mdx b/wallets/ios/chain-abstraction.mdx new file mode 100644 index 0000000..8288435 --- /dev/null +++ b/wallets/ios/chain-abstraction.mdx @@ -0,0 +1,92 @@ +--- +title: Chain Abstraction +--- + +import HowItWorks from "/snippets/walletkit/shared/chain-abstraction/intro.mdx"; +import ErrorHandling from "/snippets/walletkit/shared/chain-abstraction/error-handling.mdx"; + + + +## 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 +@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 +@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 +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. + + + +## 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](/wallets/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction-supported wallet. + + diff --git a/wallets/ios/cloud/analytics.mdx b/wallets/ios/cloud/analytics.mdx new file mode 100644 index 0000000..f78ba0b --- /dev/null +++ b/wallets/ios/cloud/analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +--- + +import Analytics from "/snippets/cloud/analytics.mdx"; + + diff --git a/wallets/ios/cloud/explorer-submission.mdx b/wallets/ios/cloud/explorer-submission.mdx new file mode 100644 index 0000000..e5f11c8 --- /dev/null +++ b/wallets/ios/cloud/explorer-submission.mdx @@ -0,0 +1,7 @@ +--- +title: Explorer Submission +--- + +import ExplorerSubmission from "/snippets/cloud/explorer-submission.mdx"; + + diff --git a/wallets/ios/cloud/relay.mdx b/wallets/ios/cloud/relay.mdx new file mode 100644 index 0000000..5f9e1c0 --- /dev/null +++ b/wallets/ios/cloud/relay.mdx @@ -0,0 +1,7 @@ +--- +title: Relay +--- + +import Relay from "/snippets/cloud/relay.mdx"; + + diff --git a/wallets/ios/eip5792.mdx b/wallets/ios/eip5792.mdx new file mode 100644 index 0000000..cdc8e64 --- /dev/null +++ b/wallets/ios/eip5792.mdx @@ -0,0 +1,284 @@ +--- +title: Wallet Call API +--- + +WalletConnect supports [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability), which defines new JSON-RPC methods that enable apps to ask a wallet to process a batch of onchain write calls and to check on the status of those calls. +Applications can specify that these onchain calls be executed taking advantage of specific capabilities previously expressed by the wallet; an additional, a novel wallet RPC is defined to enable apps to query the wallet for those capabilities. + +- `wallet_sendCalls`: Requests that a wallet submits a batch of calls. +- `wallet_getCallsStatus`: Returns the status of a call batch that was sent via wallet_sendCalls. +- `wallet_showCallsStatus`: Requests that a wallet shows information about a given call bundle that was sent with wallet_sendCalls. +- `wallet_getCapabilities`: This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication). + +## Usage + + + + ## 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 +"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 +"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 +"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 + { + "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 + { + "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 + { + "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 + { + "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 + { + "id": "...", + "capabilities": { + "caip345": { + "caip2": "eip155:1", + "transactionHashes": ["..."], + } + } + } + ``` + + Specify the `scopedProperties` when approving a session: + + ```json + "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 + { + "receipts": [/* single receipt or array of receipts */], + "atomic": true + } + ``` + + #### For Non-Atomic Execution + ```json + { + "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 +- CAIP-25 namespaces: https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md diff --git a/wallets/ios/installation.mdx b/wallets/ios/installation.mdx new file mode 100644 index 0000000..3fbed1e --- /dev/null +++ b/wallets/ios/installation.mdx @@ -0,0 +1,47 @@ +--- +title: 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 +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 +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 +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. diff --git a/wallets/ios/link-mode.mdx b/wallets/ios/link-mode.mdx new file mode 100644 index 0000000..df2393a --- /dev/null +++ b/wallets/ios/link-mode.mdx @@ -0,0 +1,43 @@ +--- +title: Link Mode +--- + +Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallets/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](/wallets/ios/one-click-auth) is implemented before enabling Link Mode. + + +```swift {5,6} +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 +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. + + diff --git a/wallets/ios/mobile-linking.mdx b/wallets/ios/mobile-linking.mdx new file mode 100644 index 0000000..73502d4 --- /dev/null +++ b/wallets/ios/mobile-linking.mdx @@ -0,0 +1,244 @@ +--- +title: Mobile Linking +--- + +import HowToTest from "/snippets/walletkit/shared/mobile-linking.mdx"; + + + +**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. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking-light.png) + + +### Sign Request Flow + +When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs: + +- **Automatic Redirect:** The Dapp automatically sends the user to their previously chosen wallet. +- **Approval Prompt:** The wallet asks the user to approve or reject the request. +- **Return to Dapp:** + - **Manual Return:** The wallet asks the user to manually return to the Dapp. + - **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. +- **User Reconnects:** Eventually, the user returns to the Dapp. + + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking_sign-light.png) + + +## Platform preparations + +In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to add your custom scheme under [`CFBundleURLTypes`](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleurltypes) key in your Info.plist file. + +```ruby +CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleURLSchemes + + examplewallet + + + +``` + + + +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 + + + + +## 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 +# 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 +# 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 +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. diff --git a/wallets/ios/notifications/notify/installation.mdx b/wallets/ios/notifications/notify/installation.mdx new file mode 100644 index 0000000..ed9852f --- /dev/null +++ b/wallets/ios/notifications/notify/installation.mdx @@ -0,0 +1,42 @@ +--- +title: Installation +--- + +Notify API is available via [Swift Package Manager](https://swift.org/package-manager/) or [Cocoapods](https://cocoapods.org/). + + + + +You can add the WalletConnect Notify package to your project with the 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 +4. Tap Add Package +5. Select `WalletConnectNotify` check mark + + + + +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 +pod 'WalletConnectSwiftV2/WalletConnectNotify' +``` + +4. Install pods with `pod install` + +If you encounter any problems during package installation, you can specify the exact path to the repository + +```ruby +pod 'WalletConnectSwiftV2/WalletConnectNotify', :git => 'https://github.com/reown-com/reown-swift.git', :tag => '1.8.0' +``` + + + + +## Next Steps + +Now that you've installed WalletConnect Notify, you're ready to start integrating it. The next section will walk you through the process of setting up your project to use the Notify API. diff --git a/wallets/ios/notifications/notify/overview.mdx b/wallets/ios/notifications/notify/overview.mdx new file mode 100644 index 0000000..cdfead0 --- /dev/null +++ b/wallets/ios/notifications/notify/overview.mdx @@ -0,0 +1,26 @@ +--- +title: Overview +--- + + +For those integrating notifications related to wallet pairing and sign requests, please check [here](../push). + + +The WalletConnect Notify API is designed to enhance the interaction between wallet users and dapps by offering a robust notification system. This API empowers wallet developers to implement a dynamic notification experience directly within their wallets. It provides the functionality for users to opt-in to notifications, ensuring they stay informed about critical events and interactions. + +The Notify API is versatile, with support for both iOS and Android platforms, making it an ideal choice for cross-platform wallet applications. + +Coupled with the AppKit Notifications, the Notify API forms part of a comprehensive toolkit that enables seamless integration of web3 communication and messaging features into dapps. This ensures a more connected and interactive experience for users in the decentralized ecosystem. + +## Features + +Some of the key features of the Notify API include: + +- **Push Notifications for Desktop and Native Platforms**: This feature enables dapps to directly send vital notifications to user wallets, ensuring timely and relevant communication. +- **Robust Spam Protection**: Users have complete authority over which dapps can send them notifications, effectively eliminating any unsolicited messages from unknown sources. Furthermore, users can fine-tune their preferences to only receive notifications types they are interested in, like new features or some important events occurrence. +- **Chain Agnostic Architecture**: The Notify API is built to be compatible with any blockchain, allowing seamless multi-chain support without the need for writing additional integration code. **As of November 2023, the Notify Server and Clients are equipped to support EVM chains. Plans to extend support to non-EVM chains are in progress and are a significant part of our upcoming development roadmap.** + +_Example integration_ + +![Web3Inbox](/images/w3i-hero.png) + \ No newline at end of file diff --git a/wallets/ios/notifications/notify/resources.mdx b/wallets/ios/notifications/notify/resources.mdx new file mode 100644 index 0000000..5979679 --- /dev/null +++ b/wallets/ios/notifications/notify/resources.mdx @@ -0,0 +1,19 @@ +--- +title: Resources +--- + +Valuable assets for developers interested in integrating Notify API into their wallet. + +- [Web3Inbox.com app](https://app.web3inbox.com) - Inbox web app that simulates wallet experience. +- [GM dapp](https://gm.walletconnect.com/) - Example dapp that sends notification every hour. +- [GM hackers](https://github.com/WalletConnect/gm-hackers) - Template used in hackathons sponsored by WalletConnect. + +## Wallet Resources + +To check more in details go and visit our [WalletKit Swift implementation app](https://github.com/reown-com/reown-swift/tree/main/Example/WalletApp). Sample Wallet 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 [our GM dapp.](https://gm.walletconnect.com/) + +## Need Technical Support?[](https://docs.reown.com/walletkit/namespaces#need-technical-support) + +If you require technical support along the way, please drop a message on the [WalletConnect GitHub](https://github.com/orgs/WalletConnect/discussions/categories/web3inbox-sdk-support) and our team will get back to you as soon as possible. diff --git a/wallets/ios/notifications/notify/spam-protection.mdx b/wallets/ios/notifications/notify/spam-protection.mdx new file mode 100644 index 0000000..d1a2bd6 --- /dev/null +++ b/wallets/ios/notifications/notify/spam-protection.mdx @@ -0,0 +1,27 @@ +--- +title: Spam Protection +--- + +Users play a critical role in web3. That’s why, with Web3Inbox, we’re committed to ensuring users can enjoy a safe, seamless, and reliable experience that puts them in the driver’s seat. As part of that pledge, Web3Inbox provides a number of user-first, anti-spam features and elements that ensure users are always in control of their web3 communications. + +## How are users protected from spam with Web3Inbox? + +### Becoming a Web3Inbox customer + +When a wallet offers app notifications to their users via Web3Inbox, the feature will always be optional. If users decide they want to receive notifications from selected apps via their wallet, they’ll be able to ‘opt-in’ and subscribe to an app’s notifications by signing a message request. Similarly, when accessing notifications through the [Web3Inbox.com app](https://app.web3inbox.com), users will be met with the same request for each application they choose to subscribe to. This feature not only enables users to experience a customized, ‘app-by-app’ approach to staying connected in web3, but also ensures they only ever hear from the apps they choose to — no unsolicited notifications or spam from unknown senders. Its their curated inbox, connected with only those they choose. + +### Setting customized notification preferences + +Once users have subscribed to their chosen apps, they have the option to define and set which types of notifications they receive from those apps. For example, a user may wish to receive only information regarding changes to their portfolio from a DEX, or, they might want to receive notifications from an NFT marketplace — but only notifications regarding their own NFT collections. In these scenarios, they’ll have the ability to disable other notification types, like marketing updates, and ensure their feed is curated to show only information that’s meaningful to them. As apps set their own notification types, they have unlimited optionality to really build out a notification structure they know can support their users’ needs — no ‘one size fits all’ approach, but a personable, community-oriented structure that puts both app and user needs’ at the forefront of communication. + +### Rate limiting + +Apps are limited to a maximum number of notifications they’re able to send to their community. Specifically, apps may send accounts notifications twice an hour on average, but may exceed that average in bursts of up to 50 at a time. + +## Our continued pledge on spam protection + +We’re constantly working on improving and growing our products, and we have a number of impactful anti-spam features and functions in the works set to increase the overall protection and user experience of Web3Inbox users: + +### User reporting + +Users will have the ability to report applications that appear to be acting or engaging with their community in a malicious or suspicious manner. Projects that are flagged as malicious may be removed from the Web3Inbox discover page and have notification functionality disabled. diff --git a/wallets/ios/notifications/notify/usage.mdx b/wallets/ios/notifications/notify/usage.mdx new file mode 100644 index 0000000..3d6743f --- /dev/null +++ b/wallets/ios/notifications/notify/usage.mdx @@ -0,0 +1,184 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + + +In this section, we showcase the aspects of using the Notify API. We'll guide you through the initial steps of initializing the Notify client and logging in a blockchain account. You'll also learn how to manage your subscriptions and messages. Additionally, we cover the process of setting up and displaying push notifications on your preferred platform. To ensure a good user experience, we include best practices for spam protection, helping you to enable the users to maintain control over the notifications wallet receives. + +## 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 Notify Client instance and initializing it with a projectId from [[WalletConnect Dashboard](https://dashboard.walletconnect.com/). +- [Account login](#account-login): + A SIWE message must be signed by the user in order to authorize the client to use Notify API +- [Subscribing to a new dapp](#subscribing-to-a-new-dapp): + Opt-in to receive notifications from dapp +- [Fetching active subscriptions](#fetching-active-subscriptions): + Get active subscriptions +- [Fetching subscription’s notification](#fetching-subscriptions-notifications): + Get notifications of a subscription +- [Updating subscriptions notification settings](#updating-subscriptions-notification-settings): + Change allowed notification types sent by dapp +- [Unsubscribe from a dapp](#unsubscribe-from-a-dapp): + Opt-out from receiving notifications from a dapp +- [Account logout](#account-logout): + To stop receiving notifications to this client, accounts can logout of using Notify API +- [Apple Push Notification service setup](?platform=ios#apple-push-notification-service-setup): + Configuring iOS app in order to decrypt notifications + +## Initialization + + + +Important: Confirm you have configured the [Network Client](https://docs.reown.com/advanced/api/core/relay) first. + +Configure the `Notify` instance with: + +```swift +try Notify.configure(environment: APNSEnvironment, crypto: CryptoProvider) +``` + +`environment` - Use `debug` environment for debug builds and `release` for release and TestFlight builds. + +`crypto` - CryptoProvider is a protocol, you are required to provide an implementation of `recoverPubKey` and `keccak256` methods. + +## Account login + +In order to register account in Notify API to be able to subscribe to any dapp to start receiving notifications, account needs to sign SIWE message to prove ownership. Developers can check if an account is registered by calling **`isRegistered()`** function. If the account is not registered, developers should call **`prepareRegistration()`** and then **`register()`** function to register the account. + +To login to manage notifications, you must request message to sign with `prepareRegistration()` method and register signature with `register()` method. Once logged in, cross-device syncing will be enabled. + +```swift +let params = try await Notify.instance.prepareRegistration(account: account, domain: "com.YOURAPPDOMAIN") +let signature = onSign(message: params.message) // Sign message with your signer +try await Notify.instance.register(params: params, signature: signature) +``` + +- `account` - An CAIP-10 account that the identity key will be issued for +- `domain` - A domain of your wallet, you should use your bundle ID + +Provide your own sign function implementation that returns CacaoSignature. If SIWE is not implemented on your app you can always use our [MessageSignerFactory](https://github.com/reown-com/reown-swift/blob/main/Sources/WalletConnectSigner/Signer/MessageSignerFactory.swift) and [DefaultSignerFactory](https://github.com/reown-com/reown-swift/blob/main/Example/Shared/DefaultSignerFactory.swift) from our sample app that uses Web3 SPM package. + +```swift +func onSign(message: String) -> CacaoSignature { + let privateKey = Data(hex: privateKey) + let signer = MessageSignerFactory(signerFactory: DefaultSignerFactory()).create() + let signature = try! signer.sign(message: message, privateKey: privateKey, type: .eip191) + return signature +} +``` + +## Subscribing to a new dapp + +To begin receiving notifications from a dapp, users must opt-in by subscribing. This subscription process grants permission for the dapp to send notifications to the user. These notifications can serve a variety of purposes, such as providing updates on the user's blockchain account activities or informing them about ongoing campaigns within the dapp. Upon initial subscription, clients will be automatically enrolled to receive all types of notifications as defined by the dapp at that moment. Users have the flexibility to modify their notification settings later, allowing them to tailor the types of alerts they receive according to their preferences. + +```swift +public func subscribe(appDomain: String, account: Account) async throws +``` + +`appDomain` - dapp domain fetched from WalletConnect explorer + +`account` - an account you want to associate a subscription with + +#### Combine event + +```swift +public var subscriptionsPublisher: AnyPublisher<[NotifySubscription], Never> +``` + +## Fetching active subscriptions + +To fetch the current list of subscriptions an account has, call **`getActiveSubscriptions()`**. + +Method will return an array of NotifySubscription objects that indicates actual subscriptions state + +```swift +public func getActiveSubscriptions(account: Account) -> [NotifySubscription] +``` + +`account` - subscriptions owner account + +## Fetching subscription’s notifications + +To fetch subscription’s notifications by calling **`getNotificationHistory()`**. + +Method will return an array of NotifyMessageRecord objects that indicates current notify messages state. This do not include old messages that aren't loaded yet. Useful for displaying initial notifications view state. For more info about pagination, check `fetchHistory` method. + +Use this method together with: + +- `messagesPublisher(topic: String)` +- `fetchHistory` + +```swift +public func getMessageHistory(topic: String) -> [NotifyMessageRecord] +``` + +`topic` - unique subscription's topic + +#### Combine events + +Publisher that send messages update event for specific topic only + +```swift +public func messagesPublisher(topic: String) -> AnyPublisher<[NotifyMessageRecord], Never> +``` + +Publisher that send event on every messages update (for all subscriptions) + +```swift +public var messagesPublisher: AnyPublisher<[NotifyMessageRecord], Never> +``` + +## Updating subscriptions notification settings + +Users can alter their notification settings to filter out unwanted alerts from a dapp. During this process, they review and select the types of notifications they wish to receive, based on the latest options provided by the dapp. + +```swift +public func update(topic: String, scope: Set) async throws +``` + +`topic` - topic of the subscription to update + +`scope` - The new space delimited list of scopes + +## Unsubscribe from a dapp + +To opt-out of receiving notifications from a dap, a user can decide to unsubscribe from dapp. + +```swift +try await Notify.instance.deleteSubscription(topic: String) +``` + +`topic` - subscription's topic + +## Account logout + +If an account is removed from the client or a user no longer wants to receive notifications for this account, you can logout the account from Notify API by calling **`unregister()`**. This will remove all subscriptions and messages for this account from the client’s storage. + +```swift +public func unregister(account: Account) async throws +``` + +`account` - account ot unregister + +## Fetch notification history (Pagination) + +Method that fetches notification history and saves it to SDK's database. When async method finishes execution, `messagesPublisher(topic: String)` will send the event with actual Notify messages for the specified topic. + +```swift +func fetchHistory(subscription: NotifySubscription, after: String?, limit: Int) async throws -> Bool +``` + +`subscription` - subscription for which notification history is requested +`after?` - id of last notification loaded. Recent notifications will be loaded if provided nil +`limit` - notifications to load count + +`Returns` - Returns True if there are still not fetched notifications + +## Apple Push Notification service setup + +To setup Apple Push Notification service please follow our [Push Notifications docs](../push). diff --git a/wallets/ios/notifications/push.mdx b/wallets/ios/notifications/push.mdx new file mode 100644 index 0000000..ba96488 --- /dev/null +++ b/wallets/ios/notifications/push.mdx @@ -0,0 +1,92 @@ +--- +title: Push Notifications +--- + +WalletKit provides the functionality for wallets to receive push notifications through Firebase Cloud Messaging (FCM) and Apple Push Notification Service (APNs) via the Push Server. This feature ensures that wallets are promptly notified of incoming signature requests. Each push notification contains the encrypted details of the signature request. Upon receiving the notification, it can be decrypted and presented to the developer, allowing for customization of the message according to their requirements. + +## Server setup + +For the push notifications to be forwarded to FCM or APNs, the [Push Server](https://docs.reown.com/advanced/push-server) will need to be configured with your FCM or APNs server API credentials. + +## App setup + +### Register the device token + +To enable a device for push notifications, it's essential to register the device token using `WalletKit.registerDeviceToken`. This token can be obtained from either FCM or APNS, depending on the platform used. + +In your AppDelegate, you need to register your device token for push notifications. To enable encrypted push notifications, set the `enableEncrypted` flag to `true`. + +```Swift +func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + Task(priority: .high) { + try await WalletKit.instance.register(deviceToken: deviceToken, enableEncrypted: true) + } +} +``` + +### Receiving push notifications + +After the device token is registered, the next step involves setting up the notification service specific to the platform being used. This service will decrypt the incoming requests and forward them to the developer for further processing and integration. + +When using encrypted push notifications via APNs, the payload will look like this: + +```json +{ + "aps": { + "content-available": 1, + "mutable-content": 1 + }, + "message": "String", // Encrypted payload + "topic": "String", // Subscription topic + "tag": "String" // Tag of the associated relay message +} +``` + +To decrypt a push notification, follow these steps: + +1. Instantiate [UNNotificationServiceExtension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) + +2. Modify the content of newly delivered notifications. + Learn more about [modifying content in newly delivered notifications](https://developer.apple.com/documentation/usernotifications/modifying_content_in_newly_delivered_notifications). + +3. Create a Shared [Keychain Group](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps) + +Ensure you have a keychain group that is shared between your wallet application and the notification service. This is set in the app during the Networking Client configuration as shown below: + +```Swift +Networking.configure( + groupIdentifier: "group.com.walletconnect.sdk", + projectId: InputConfig.projectId, + socketFactory: DefaultSocketFactory() +) +``` + +4. Instantiate WalletKitDecryptionService + +Use the same group name inside your notification service extension. + +```Swift +override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { + self.contentHandler = contentHandler + self.bestAttemptContent = request.content + + if let content = bestAttemptContent, + let topic = content.userInfo["topic"] as? String, + let ciphertext = content.userInfo["message"] as? String, + let tag = content.userInfo["tag"] as? UInt { + + if WalletKitDecryptionService.canHandle(tag: tag) { + let mutableContent = handleWalletKitNotification(content: content, topic: topic, tag: tag, ciphertext: ciphertext) + contentHandler(mutableContent) + } else if NotifyDecryptionService.canHandle(tag: tag) { + let mutableContent = handleNotifyNotification(content: content, topic: topic, ciphertext: ciphertext) + contentHandler(mutableContent) + } else { + let mutableContent = content.mutableCopy() as! UNMutableNotificationContent + mutableContent.title = "Error: unknown message tag" + } + } + } +``` + +`handleWalletKitNotification` and `handleNotifyNotification` methods can be found in our [Sample App](https://github.com/reown-com/reown-swift/blob/main/Example/PNDecryptionService/NotificationService.swift) \ No newline at end of file diff --git a/wallets/ios/one-click-auth.mdx b/wallets/ios/one-click-auth.mdx new file mode 100644 index 0000000..a503349 --- /dev/null +++ b/wallets/ios/one-click-auth.mdx @@ -0,0 +1,102 @@ +--- +title: One-click Auth +--- + +## Introduction + +This section outlines an innovative protocol method that facilitates the initiation of a Sign session and the authentication of a wallet through a [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361) (SIWE) message, enhanced by [ReCaps](https://eips.ethereum.org/EIPS/eip-5573) (ReCap Capabilities). + +This enhancement not only offers immediate authentication for dApps, paving the way for prompt user logins, but also integrates informed consent for authorization. Through this mechanism, dApps can request the delegation of specific capabilities to perform actions on behalf of the wallet user. These capabilities, encapsulated within SIWE messages as ReCap URIs, detail the scope of actions authorized by the user in an explicit and human-readable form. + +By incorporating ReCaps, this method extends the utility of SIWE messages, allowing dApps to combine authentication with a nuanced authorization model. This model specifies the actions a dApp is authorized to execute on the user's behalf, enhancing security and user autonomy by providing clear consent for each delegated capability. As a result, dApps can utilize these consent-backed messages to perform predetermined actions, significantly enriching the interaction between dApps, wallets, and users within the Ethereum ecosystem. + +![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/authenticatedSessions-light.png) + + + +## Handling Authentication Requests + +To handle incoming authentication requests, 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 +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 +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 +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 +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. + + diff --git a/wallets/ios/resources.mdx b/wallets/ios/resources.mdx new file mode 100644 index 0000000..cb6ed48 --- /dev/null +++ b/wallets/ios/resources.mdx @@ -0,0 +1,28 @@ +--- +title: 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/)) + diff --git a/wallets/ios/usage.mdx b/wallets/ios/usage.mdx new file mode 100644 index 0000000..5af6f24 --- /dev/null +++ b/wallets/ios/usage.mdx @@ -0,0 +1,442 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 + + + +## 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 +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 +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 +[ + { + "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 +public static func build( + sessionProposal: Session.Proposal, + chains: [Blockchain], + methods: [String], + events: [String], + accounts: [Account] +) throws -> [String: SessionNamespace] +``` + +Example usage + +```swift +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 +{ + //... + 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 + WalletKit.instance.approve( + proposalId: "proposal_id", + namespaces: sessionNamespaces +) +``` + +When session is successfully approved `sessionsPublishers` will publish a `Session` + +```swift +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 +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/, which is v2 protocol compliant. +Once you derive a URI from the QR code call `pair` method: + +```swift +try await WalletKit.instance.pair(uri: uri) +``` + +if everything goes well, you should handle following event: + +```swift +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 +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 +{ + "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 +{ + "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 +WalletKit.instance.sessionsPublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] sessions in + // Reload UI + }.store(in: &publishers) +``` + +### Session Rejection + +```swift +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 +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 +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 +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 +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 +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 +try await WalletKit.instance.disconnect(topic: session.topic) +``` + +### Subscribe for WalletKit Publishers + +The following publishers are available to subscribe: + +```swift +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 + +WalletKit.instance.register(deviceToken: deviceToken, enableEncrypted: true) + +``` diff --git a/wallets/ios/verify.mdx b/wallets/ios/verify.mdx new file mode 100644 index 0000000..672c8b7 --- /dev/null +++ b/wallets/ios/verify.mdx @@ -0,0 +1,50 @@ +--- +title: 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. + +These are: + +![Verify Banner](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/verify-banner.png) + + +## 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 +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 +} +``` diff --git a/wallets/more/best-practices.mdx b/wallets/more/best-practices.mdx new file mode 100644 index 0000000..4cca006 --- /dev/null +++ b/wallets/more/best-practices.mdx @@ -0,0 +1,748 @@ +--- +sidebarTitle: Best Practices +title: Best Practices for Wallets +--- + +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. + + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/connection-successful.png) + + +### 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 + const uri = 'xxx'; // pairing uri + try { + await walletKit.pair({ uri }); + } catch (error) { + // some error happens while pairing - check Expected errors section + } + ``` + + + + ```jsx + const uri = 'xxx'; // pairing uri + try { + await walletKit.pair({ uri }); + } catch (error) { + // some error happens while pairing - check Expected errors section + } + ``` + + + + ```swift + let uri = WalletConnectURI(string: urlString) + + if let uri { + Task { + try await WalletKit.instance.pair(uri: uri) + } + } + + ``` + + + + ```kotlin + 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 + core.pairing.events.on("pairing_expire", (event) => { + // pairing expired before user approved/rejected a session proposal + const { topic } = topic; + }); + ``` + + + + ```typescript + core.pairing.events.on("pairing_expire", (event) => { + // pairing expired before user approved/rejected a session proposal + const { topic } = topic; + }); + ``` + + + + ```Swift + 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 + 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 + try { + await walletKit.approveSession(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } + ``` + + + + ```typescript + try { + await walletKit.approveSession(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } + ``` + + + + ```swift + do { + try await WalletKit.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties) + // Update UI, remove loader + } catch { + // present error + } + ``` + + + + ```kotlin + 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 + try { + await walletKit.rejectSession(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } + ``` + + + + ```typescript + try { + await walletKit.rejectSession(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } + ``` + + + + ```swift + do { + try await WalletKit.instance.reject(proposalId: proposal.id, reason: .userRejected) + // Update UI, remove loader + } catch { + // present error + } + ``` + + + + ```kotlin + 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 + walletKit.on("proposal_expire", (event) => { + // proposal expired and any modal displaying it should be removed + const { id } = event; + }); + ``` + + + + ```typescript + walletKit.on("proposal_expire", (event) => { + // proposal expired and any modal displaying it should be removed + const { id } = event; + }); + ``` + + + + ```swift + WalletKit.instance.sessionProposalExpirationPublisher.sink { _ in + // let user know that session proposal has expired, update UI + }.store(in: &publishers) + ``` + + + + ```kotlin + 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 + try { + await walletKit.respondSessionRequest(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } + ``` + + + + ```typescript + try { + await walletKit.respondSessionRequest(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } + ``` + + + + ```swift + 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 + 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 + walletKit.on("session_request_expire", (event) => { + // request expired and any modal displaying it should be removed + const { id } = event; + }); + ``` + + + + ```typescript + walletKit.on("session_request_expire", (event) => { + // request expired and any modal displaying it should be removed + const { id } = event; + }); + ``` + + + + ```swift + WalletKit.instance.requestExpirationPublisher.sink { _ in + // let user know that request has expired + }.store(in: &publishers) + ``` + + + + ```kotlin + 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 + 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 + 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 + WalletKit.instance.socketConnectionStatusPublisher + .receive(on: DispatchQueue.main) + .sink { status in + switch status { + case .connected: + // ... + case .disconnected: + // ... + } + }.store(in: &publishers) + ``` + + + + ```kotlin + 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. + + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking-light.png) + + +### Sign Request Flow + +When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs: + +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. + + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking_sign-light.png) + + +### 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 States](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/verify-states-1.png) + + + + ![Verify States](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/verify-states-2.png) + + + + 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 + + + ![Verify API flagged domain](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/verify-api-flagged-domain.png) + + +*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. + +![Subscribe to releases](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/subsribe-to-release-updates.png) + +## 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 diff --git a/wallets/more/updating-wallet-sdk.mdx b/wallets/more/updating-wallet-sdk.mdx new file mode 100644 index 0000000..75a4bd5 --- /dev/null +++ b/wallets/more/updating-wallet-sdk.mdx @@ -0,0 +1,41 @@ +--- +title: 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) diff --git a/wallets/more/web3wallet-migration/android.mdx b/wallets/more/web3wallet-migration/android.mdx new file mode 100644 index 0000000..b81ef18 --- /dev/null +++ b/wallets/more/web3wallet-migration/android.mdx @@ -0,0 +1,107 @@ +--- +title: Upgrade from Web3Wallet to WalletKit for Android +sidebarTitle: WalletKit - 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 +/* 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 +/* 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 +/* 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 +/* 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. \ No newline at end of file diff --git a/wallets/more/web3wallet-migration/flutter.mdx b/wallets/more/web3wallet-migration/flutter.mdx new file mode 100644 index 0000000..e1f8331 --- /dev/null +++ b/wallets/more/web3wallet-migration/flutter.mdx @@ -0,0 +1,79 @@ +--- +title: Upgrade from Web3Wallet to WalletKit for Flutter +sidebarTitle: WalletKit - 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 +/* 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 +/* 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 + + + +### Step 3. Update error definitions + +```tsx +/* highlight-delete-start */ +Errors.getSdkError(Errors.USER_REJECTED); +/* highlight-delete-end */ +/* highlight-add-start */ +Errors.getSdkError(Errors.USER_REJECTED).toSignError(); +/* highlight-add-end */ +``` + +### Step 5. Update any exception type + +
+ +### 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 diff --git a/wallets/more/web3wallet-migration/ios.mdx b/wallets/more/web3wallet-migration/ios.mdx new file mode 100644 index 0000000..4bfaab1 --- /dev/null +++ b/wallets/more/web3wallet-migration/ios.mdx @@ -0,0 +1,114 @@ +--- +title: Upgrade from Web3Wallet to WalletKit for iOS +sidebarTitle: WalletKit - 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 +/* 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 +/* 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 +/* 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 +/* 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 +/* 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. \ No newline at end of file diff --git a/wallets/more/web3wallet-migration/quickstart.mdx b/wallets/more/web3wallet-migration/quickstart.mdx new file mode 100644 index 0000000..82f7d5f --- /dev/null +++ b/wallets/more/web3wallet-migration/quickstart.mdx @@ -0,0 +1,32 @@ +--- +title: Upgrade from Web3Wallet to Reown WalletKit +sidebarTitle: Overview +--- + +## 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. + + diff --git a/wallets/more/web3wallet-migration/react-native.mdx b/wallets/more/web3wallet-migration/react-native.mdx new file mode 100644 index 0000000..1ffb7ee --- /dev/null +++ b/wallets/more/web3wallet-migration/react-native.mdx @@ -0,0 +1,66 @@ +--- +title: Upgrade from Web3Wallet to WalletKit for React Native +sidebarTitle: WalletKit - 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 +/* 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 +/* 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 +/* 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 +/* 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](/wallets/web/usage) ++ `auth_request` is deprecated in favor of `session_authenticate`. Docs can be found [here](/wallets/web/one-click-auth) diff --git a/wallets/more/web3wallet-migration/unity.mdx b/wallets/more/web3wallet-migration/unity.mdx new file mode 100644 index 0000000..6a1575d --- /dev/null +++ b/wallets/more/web3wallet-migration/unity.mdx @@ -0,0 +1,98 @@ +--- +title: Upgrade from Web3Wallet to WalletKit for .NET +sidebarTitle: WalletKit - 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 + + + + + +/* highlight-delete-start */ + +/* highlight-delete-end */ +/* highlight-add-start */ + +/* highlight-add-end */ + + + +``` + +Alternatively, you can use the .NET CLI: + +```bash +# 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. diff --git a/wallets/more/web3wallet-migration/web.mdx b/wallets/more/web3wallet-migration/web.mdx new file mode 100644 index 0000000..af87de0 --- /dev/null +++ b/wallets/more/web3wallet-migration/web.mdx @@ -0,0 +1,65 @@ +--- +title: Upgrade from Web3Wallet to WalletKit for Web +sidebarTitle: WalletKit - 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 +/* 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 +/* 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 +/* 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 +/* 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](/wallets/web/usage) ++ `auth_request` is deprecated in favor of `session_authenticate`. Docs can be found [here](/wallets/web/one-click-auth) diff --git a/wallets/overview.mdx b/wallets/overview.mdx new file mode 100644 index 0000000..6668fca --- /dev/null +++ b/wallets/overview.mdx @@ -0,0 +1,63 @@ +--- +title: WalletConnect Wallet SDK +sidebarTitle: Quickstart +--- + +**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 + + + ![Wallet SDK banner](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/walletkit.png) + + +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. diff --git a/wallets/react-native/best-practices.mdx b/wallets/react-native/best-practices.mdx new file mode 100644 index 0000000..b6dcf62 --- /dev/null +++ b/wallets/react-native/best-practices.mdx @@ -0,0 +1,198 @@ +--- +title: 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 +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 +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 + try { + await walletKit.approveSession(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } +``` +Rejecting session +```typescript + 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 +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 + 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 +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 +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 + + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/connection_state.gif) + diff --git a/wallets/react-native/chain-abstraction.mdx b/wallets/react-native/chain-abstraction.mdx new file mode 100644 index 0000000..6d74bf8 --- /dev/null +++ b/wallets/react-native/chain-abstraction.mdx @@ -0,0 +1,96 @@ +--- +title: Chain Abstraction +--- + +import HowItWorks from "/snippets/walletkit/shared/chain-abstraction/intro.mdx"; +import ErrorHandling from "/snippets/walletkit/shared/chain-abstraction/error-handling.mdx"; + + + +## 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 +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 +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 +// 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. + + + +## Testing + +To test Chain Abstraction, you can use the [AppKit laboratory](https://appkit-lab.reown.com/library/wagmi/) and try sending any supported [tokens](/wallets/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction supported wallet. +You can also use this [sample wallet](https://appdistribution.firebase.dev/i/076a3bc9669d3bee) for testing. + + diff --git a/wallets/react-native/cloud/analytics.mdx b/wallets/react-native/cloud/analytics.mdx new file mode 100644 index 0000000..f78ba0b --- /dev/null +++ b/wallets/react-native/cloud/analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +--- + +import Analytics from "/snippets/cloud/analytics.mdx"; + + diff --git a/wallets/react-native/cloud/explorer-submission.mdx b/wallets/react-native/cloud/explorer-submission.mdx new file mode 100644 index 0000000..e5f11c8 --- /dev/null +++ b/wallets/react-native/cloud/explorer-submission.mdx @@ -0,0 +1,7 @@ +--- +title: Explorer Submission +--- + +import ExplorerSubmission from "/snippets/cloud/explorer-submission.mdx"; + + diff --git a/wallets/react-native/cloud/relay.mdx b/wallets/react-native/cloud/relay.mdx new file mode 100644 index 0000000..5f9e1c0 --- /dev/null +++ b/wallets/react-native/cloud/relay.mdx @@ -0,0 +1,7 @@ +--- +title: Relay +--- + +import Relay from "/snippets/cloud/relay.mdx"; + + diff --git a/wallets/react-native/eip5792.mdx b/wallets/react-native/eip5792.mdx new file mode 100644 index 0000000..4643606 --- /dev/null +++ b/wallets/react-native/eip5792.mdx @@ -0,0 +1,284 @@ +--- +title: Wallet Call API +--- + +WalletConnect supports [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability), which defines new JSON-RPC methods that enable apps to ask a wallet to process a batch of onchain write calls and to check on the status of those calls. +Applications can specify that these onchain calls be executed taking advantage of specific capabilities previously expressed by the wallet; an additional, a novel wallet RPC is defined to enable apps to query the wallet for those capabilities. + +- `wallet_sendCalls`: Requests that a wallet submits a batch of calls. +- `wallet_getCallsStatus`: Returns the status of a call batch that was sent via wallet_sendCalls. +- `wallet_showCallsStatus`: Requests that a wallet shows information about a given call bundle that was sent with wallet_sendCalls. +- `wallet_getCapabilities`: This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication). + +## Usage + + + + ## 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 +"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 +"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 +"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 + { + "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 + { + "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 + { + "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 + { + "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 + { + "id": "...", + "capabilities": { + "caip345": { + "caip2": "eip155:1", + "transactionHashes": ["..."], + } + } + } + ``` + + Specify the `scopedProperties` when approving a session: + + ```json + "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 + { + "receipts": [/* single receipt or array of receipts */], + "atomic": true + } + ``` + + #### For Non-Atomic Execution + ```json + { + "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 +- CAIP-25 namespaces: https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md diff --git a/wallets/react-native/installation.mdx b/wallets/react-native/installation.mdx new file mode 100644 index 0000000..a3d785d --- /dev/null +++ b/wallets/react-native/installation.mdx @@ -0,0 +1,31 @@ +--- +title: Installation +--- + +Install the WalletKit package. + +```sh +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 +yarn add @react-native-async-storage/async-storage @react-native-community/netinfo react-native-get-random-values fast-text-encoding +``` + + +```sh +npx expo install expo-application +``` + + +For those using Typescript, we recommend adding these dev dependencies: + +```sh +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. diff --git a/wallets/react-native/link-mode.mdx b/wallets/react-native/link-mode.mdx new file mode 100644 index 0000000..72b132c --- /dev/null +++ b/wallets/react-native/link-mode.mdx @@ -0,0 +1,115 @@ +--- +title: Link Mode +--- + +Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallets/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](/wallets/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} +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 +#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 + + + + + com.apple.developer.associated-domains + + applinks: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 + + + + + + + + + + +``` + +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. diff --git a/wallets/react-native/mobile-linking.mdx b/wallets/react-native/mobile-linking.mdx new file mode 100644 index 0000000..6b9cb66 --- /dev/null +++ b/wallets/react-native/mobile-linking.mdx @@ -0,0 +1,115 @@ +--- +title: Mobile Linking +--- + +import HowToTest from "/snippets/walletkit/shared/mobile-linking.mdx"; + + + +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. + + + + Mobile Linking Connect Flow + Mobile Linking Connect Flow + + +### 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. + + + Mobile Linking Connect Flow + Mobile Linking Connect Flow + + +## 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 + + + +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 + + + + + +## 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 +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 + } +} +``` diff --git a/wallets/react-native/notifications/notify/installation.mdx b/wallets/react-native/notifications/notify/installation.mdx new file mode 100644 index 0000000..a3ad2ad --- /dev/null +++ b/wallets/react-native/notifications/notify/installation.mdx @@ -0,0 +1,114 @@ +--- +title: Installation +--- + +Install the WalletConnect NotifyClient package. + +```sh +yarn add @walletconnect/notify-client @walletconnect/react-native-compat +``` + +You will need to polyfill crypto depending on your environment. See instructions below. + + + + +```sh +yarn add expo-crypto +``` + +1. Create a file called `expo-crypto-shim.js` at the root of your project +2. Go to `expo-crypto-shim.js`and paste the following snippet into it. + +```js +import { digest } from "expo-crypto"; + +// eslint-disable-next-line no-undef +const webCrypto = typeof crypto !== "undefined" ? crypto : new Crypto(); +webCrypto.subtle = { + digest: (algo, data) => { + const buf = Buffer.from(data); + return digest(algo, buf); + }, +}; +(() => { + if (typeof crypto === "undefined") { + Object.defineProperty(window, "crypto", { + configurable: true, + enumerable: true, + get: () => webCrypto, + }); + } +})(); +``` + +3. Then head over your `index.js` file at the root of your project and add the following imports. + +```js +import "@walletconnect/react-native-compat"; +import "./expo-crypto-shim.js"; +``` + + + + +```sh +yarn add react-native-quick-crypto react-native-quick-base64 stream-browserify @craftzdog/react-native-buffer babel-plugin-module-resolver +``` + +For iOS only + +```bash +cd ios && pod install +``` + +1. Go to your `index.js` file at the root of your project and add the following polyfill + +```js +import { AppRegistry } from "react-native"; +import App from "./App"; +import { name as appName } from "./app.json"; +import crypto from "react-native-quick-crypto"; + +const polyfillDigest = async (algorithm, data) => { + const algo = algorithm.replace("-", "").toLowerCase(); + const hash = crypto.createHash(algo); + hash.update(data); + return hash.digest(); +}; + +globalThis.crypto = crypto; +globalThis.crypto.subtle = { + digest: polyfillDigest, +}; + +AppRegistry.registerComponent(appName, () => App); +``` + +2. Update your `babel.config.js` with the following configuration + +```js +module.exports = { + presets: ['module:metro-react-native-babel-preset'], + plugins: [ + [ + 'module-resolver', + { + alias: { + 'crypto': 'react-native-quick-crypto', + 'stream': 'stream-browserify', + 'buffer': '@craftzdog/react-native-buffer', + }, + }, + ], + ... + ], +}; +``` + + + + +## Next Steps + +Now that you've installed WalletConnect Notify, you're ready to start integrating it. The next section will walk you through the process of setting up your project to use the Notify API. diff --git a/wallets/react-native/notifications/notify/overview.mdx b/wallets/react-native/notifications/notify/overview.mdx new file mode 100644 index 0000000..3c1bf27 --- /dev/null +++ b/wallets/react-native/notifications/notify/overview.mdx @@ -0,0 +1,26 @@ +--- +title: Overview +--- + + +For those integrating notifications related to wallet pairing and sign requests, please check [here](../push). + + +The WalletKit Notify API is designed to enhance the interaction between wallet users and dapps by offering a robust notification system. This API empowers wallet developers to implement a dynamic notification experience directly within their wallets. It provides the functionality for users to opt-in to notifications, ensuring they stay informed about critical events and interactions. + +The Notify API is versatile, with support for both iOS and Android platforms, making it an ideal choice for cross-platform wallet applications. + +Coupled with the AppKit Notifications, the Notify API forms part of a comprehensive toolkit that enables seamless integration of web3 communication and messaging features into dapps. This ensures a more connected and interactive experience for users in the decentralized ecosystem. + +## Features + +Some of the key features of the Notify API include: + +- **Push Notifications for Desktop and Native Platforms**: This feature enables dapps to directly send vital notifications to user wallets, ensuring timely and relevant communication. +- **Robust Spam Protection**: Users have complete authority over which dapps can send them notifications, effectively eliminating any unsolicited messages from unknown sources. Furthermore, users can fine-tune their preferences to only receive notifications types they are interested in, like new features or some important events occurrence. +- **Chain Agnostic Architecture**: The Notify API is built to be compatible with any blockchain, allowing seamless multi-chain support without the need for writing additional integration code. **As of November 2023, the Notify Server and Clients are equipped to support EVM chains. Plans to extend support to non-EVM chains are in progress and are a significant part of our upcoming development roadmap.** + +_Example integration_ + + + diff --git a/wallets/react-native/notifications/notify/spam-protection.mdx b/wallets/react-native/notifications/notify/spam-protection.mdx new file mode 100644 index 0000000..0e7bc21 --- /dev/null +++ b/wallets/react-native/notifications/notify/spam-protection.mdx @@ -0,0 +1,27 @@ +--- +title: Spam Protection +--- + +Users play a critical role in web3. That's why, with Web3Inbox, we’re committed to ensuring users can enjoy a safe, seamless, and reliable experience that puts them in the driver’s seat. As part of that pledge, Web3Inbox provides a number of user-first, anti-spam features and elements that ensure users are always in control of their web3 communications. + +## How are users protected from spam with Web3Inbox? + +### Becoming a WalletKit Notification customer + +When a wallet offers app notifications to their users via Web3Inbox, the feature will always be optional. If users decide they want to receive notifications from selected apps via their wallet, they’ll be able to ‘opt-in’ and subscribe to an app’s notifications by signing a message request. Similarly, when accessing notifications through the [Web3Inbox.com app](https://app.web3inbox.com), users will be met with the same request for each application they choose to subscribe to. This feature not only enables users to experience a customized, ‘app-by-app’ approach to staying connected in web3, but also ensures they only ever hear from the apps they choose to — no unsolicited notifications or spam from unknown senders. Its their curated inbox, connected with only those they choose. + +### Setting customized notification preferences + +Once users have subscribed to their chosen apps, they have the option to define and set which types of notifications they receive from those apps. For example, a user may wish to receive only information regarding changes to their portfolio from a DEX, or, they might want to receive notifications from an NFT marketplace — but only notifications regarding their own NFT collections. In these scenarios, they’ll have the ability to disable other notification types, like marketing updates, and ensure their feed is curated to show only information that’s meaningful to them. As apps set their own notification types, they have unlimited optionality to really build out a notification structure they know can support their users’ needs — no ‘one size fits all’ approach, but a personable, community-oriented structure that puts both app and user needs’ at the forefront of communication. + +### Rate limiting + +Apps are limited to a maximum number of notifications they’re able to send to their community. Specifically, apps may send accounts notifications twice an hour on average, but may exceed that average in bursts of up to 50 at a time. + +## Our continued pledge on spam protection + +We're constantly working on improving and growing our products, and we have a number of impactful anti-spam features and functions in the works set to increase the overall protection and user experience of WalletKit Notification users: + +### User reporting + +Users will have the ability to report applications that appear to be acting or engaging with their community in a malicious or suspicious manner. Projects that are flagged as malicious may be removed from the WalletKit Notification discover page and have notification functionality disabled. diff --git a/wallets/react-native/notifications/notify/usage.mdx b/wallets/react-native/notifications/notify/usage.mdx new file mode 100644 index 0000000..0bc1322 --- /dev/null +++ b/wallets/react-native/notifications/notify/usage.mdx @@ -0,0 +1,427 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +In this section, we showcase the aspects of using the Notify API. We'll guide you through the initial steps of initializing the Notify client and logging in a blockchain account. You'll also learn how to manage your subscriptions and messages. Additionally, we cover the process of setting up and displaying push notifications on your preferred platform. To ensure a good user experience, we include best practices for spam protection, helping you to enable the users to maintain control over the notifications wallet receives. + +## 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 Notify Client instance and initializing it with a projectId from [[WalletConnect Dashboard](https://dashboard.walletconnect.com/). +- [Account login](#account-login): + A SIWE message must be signed by the user in order to authorize the client to use Notify API +- [Subscribing to a new dapp](#subscribing-to-a-new-dapp): + Opt-in to receive notifications from dapp +- [Fetching active subscriptions](#fetching-active-subscriptions): + Get active subscriptions +- [Fetching subscription’s notification](#fetching-subscriptions-notifications): + Get notifications of a subscription +- [Fetching available notification types](#fetching-available-notification-types): + Get latest notification types +- [Updating subscriptions notification settings](#updating-subscriptions-notification-settings): + Change allowed notification types sent by dapp +- [Unsubscribe from a dapp](#unsubscribe-from-a-dapp): + Opt-out from receiving notifications from a dapp +- [Account logout](#account-logout): + To stop receiving notifications to this client, accounts can logout of using Notify API +- [Push Notification Setup](#push-notification-setup): + Configuring app in order to decrypt notifications + +## Initialization + + + +#### Initialize the SDK clients + +```javascript +import { NotifyClient } from "@walletconnect/notify-client"; + +const notifyClient = await NotifyClient.init({ + projectId: "", +}); +``` + +## Add listeners for relevant events + +```javascript +// Handle response to a `notifyClient.subscribe(...)` call +notifyClient.on("notify_subscription", async ({ params }) => { + const { error } = params; + + if (error) { + // Setting up the subscription failed. + // Inform the user of the error and/or clean up app state. + console.error("Setting up subscription failed: ", error); + } else { + // New subscription was successfully created. + // Inform the user and/or update app state to reflect the new subscription. + console.log(`Subscribed successfully.`); + } +}); + +// Handle an incoming notification +notifyClient.on("notify_message", ({ params }) => { + const { message } = params; + // e.g. build a notification using the metadata from `message` and show to the user. +}); + +// Handle response to a `notifyClient.update(...)` call +notifyClient.on("notify_update", ({ params }) => { + const { error } = params; + + if (error) { + // Updating the subscription's scope failed. + // Inform the user of the error and/or clean up app state. + console.error("Setting up subscription failed: ", error); + } else { + // Subscription's scope was updated successfully. + // Inform the user and/or update app state to reflect the updated subscription. + console.log(`Successfully updated subscription scope.`); + } +}); + +// Handle a change in the existing subscriptions (e.g after a subscribe or update) +notifyClient.on("notify_subscriptions_changed", ({ params }) => { + const { subscriptions } = params; + // `subscriptions` will contain any *changed* subscriptions since the last time this event was emitted. + // To get a full list of subscriptions for a given account you can use `notifyClient.getActiveSubscriptions({ account: 'eip155:1:0x63Be...' })` +}); +``` + +## Account login + +In order to register account in Notify API to be able to subscribe to any dapp to start receiving notifications, account needs to sign SIWE message to prove ownership. Developers can check if an account is registered by calling **`isRegistered()`** function. If the account is not registered, developers should call **`prepareRegistration()`** and then **`register()`** function to register the account. + + +This is a one-time action per account. It does not need to be repeated after initial registration of the new account. + + +### Registering as a wallet + +```javascript +const account = `eip155:1:0x63Be2c680685d2A9620c11b0068291261aa62d76` +const domain = 'app.mydomain.com', // pass the domain (i.e. the hostname) where your dapp is hosted. +const allApps = true // The user will be prompted to authorize this wallet to send and receive messages on their behalf for ALL domains using their WalletConnect identity. + +// No need to register and sign message if already registered. +if (notifyClient.isRegistered({ account, domain, allApps })) return; + +const {registerParams, message} = notifyClient.prepareRegistration({ + account, + domain, + allApps +}); + +const signature = await ethersWallet.signMessage(message); + +await notifyClient.register({ + registerParams, + signature, +}) +``` + +## Subscribing to a new dapp + +To begin receiving notifications from a dapp, users must opt-in by subscribing. This subscription process grants permission for the dapp to send notifications to the user. These notifications can serve a variety of purposes, such as providing updates on the user's blockchain account activities or informing them about ongoing campaigns within the dapp. Upon initial subscription, clients will be automatically enrolled to receive all types of notifications as defined by the dapp at that moment. Users have the flexibility to modify their notification settings later, allowing them to tailor the types of alerts they receive according to their preferences. + + +To identify dapps that can be subscribed to via Notify, we can query the following Explorer API endpoint: + +https://explorer-api.walletconnect.com/v3/dapps?projectId=YOUR_PROJECT_ID&is_notify_enabled=true + + +```javascript +// Get the domain of the target dapp from the Explorer API response +const appDomain = new URL(fetchedExplorerDapp.platform_browser).hostname; + +// Subscribe to `fetchedExplorerDapp` by passing the account to be subscribed and the domain of the target dapp. +await notifyClient.subscribe({ + account, + appDomain, +}); + +// -> Success/Failure will be received via the `notify_update` event registered previously. +// -> New subscription will be emitted via the `notify_subscriptions_changed` watcher event. +``` + +## Fetching active subscriptions + +To fetch the current list of subscriptions an account has, call **`getActiveSubscriptions()`**. + +```javascript +// Will return all active subscriptions for the provided account, keyed by subscription topic. +const accountSubscriptions = notifyClient.getActiveSubscriptions({ + account: `eip155:1:0x63Be...`, +}); +``` + +## Fetching subscription's notifications + +To fetch subscription's notifications by calling **`getNotificationHistory()`**. + +```javascript +const notifications = notifyClient.getNotificationHistory(account); +``` + +## Fetching available notification types + +Developers can fetch latest notification types specified by dapp by calling **`getNotificationTypes()`** function. + +You can use the `scope` object of the subscription to get the available notification types. + +```typescript +// get notification types by accessing `scope` member of a dapp's subscription +const notificationTypes = notifyClient + .getActiveSubscriptions({ account }) + .filter((subscription) => subscription.topic === topic).scope; +``` + +## Updating subscriptions notification settings + +Users can alter their notification settings to filter out unwanted alerts from a dapp. During this process, they review and select the types of notifications they wish to receive, based on the latest options provided by the dapp. Available notification types fetching is shown in the [next section](#fetching-available-notification-types). + +```javascript +// `topic` - subscription topic of the subscription that should be updated. +// `scope` - an array of notification types that should be enabled going forward. The current scopes can be found under `subscription.scope`. +await notifyClient.update({ + topic, + scope: ["alerts"], +}); +``` + +## Unsubscribe from a dapp + +To opt-out of receiving notifications from a dap, a user can decide to unsubscribe from dapp. + +```javascript +notifyClient.deleteSubscription({ + topic: "subscription_topic_to_unsubscribe_from", +}); +``` + +## Account logout + +If an account is removed from the client or a user no longer wants to receive notifications for this account, you can logout the account from Notify API by calling **`unregister()`**. This will remove all subscriptions and messages for this account from the client’s storage. + +```javascript +const account = `eip155:1:0x63Be2c680685d2A9620c11b0068291261aa62d76`; + +await notifyClient.unregister({ + account, +}); +``` + +## Fetch notification history (Pagination) + +There might be different approaches to implement pagination in your app depending on your needs. You can see the following example implemented with `FlatList` which introduces infinite scroll functionality with a basic example: + +Please make sure you have better handling of the notify client instance which handles worst cases by checking initialization status, account status, for production ready apps. + +```javascript +export default function SubscriptionDetailsScreen() { + const {topic} = useRoute().params as {topic: string}; + const [notifications, setNotifications] = React.useState([]); + const [hasMore, setHasMore] = React.useState(false); + const [isLoading, setIsLoading] = React.useState(false); + + const lastItem = notifications?.[notifications.length - 1]?.id; + + async function getNotificationHistory(startingAfter?: string) { + setIsLoading(true); + + const notificationHistory = await notifyClient.getNotificationHistory({ + topic, + limit: 15, + startingAfter, + }); + + setNotifications( + prevNotifications => prevNotifications.concat(notificationHistory.notifications), + ); + setHasMore(notificationHistory.hasMore); + setIsLoading(false); + + return notificationHistory; + } + + React.useEffect(() => { + getNotificationHistory(); + }, [topic]); + + return ( + item.sentAt.toString()} + onEndReached={() => { + if (hasMore && lastItem) { + getNotificationHistory(lastItem) + } + }} + ListFooterComponent={() => { + if (!isLoading) return null + return + }} + renderItem={({item}) => ( + + )} + /> + ); +} +``` + +### Push Notification Setup + +Install [`@react-native-firebase/app`](https://www.npmjs.com/package/@react-native-firebase/app), [`@react-native-firebase/messaging`](https://www.npmjs.com/package/@react-native-firebase/messaging) and [`@notifee/react-native`](https://www.npmjs.com/package/@notifee/react-native) to handle Push Notifications. +Please refer to the respective package documentation to configure them properly. + +``` +yarn add @notifee/react-native @react-native-firebase/app @react-native-firebase/messaging +``` + +Update your `index.js` file to include the following logic. + +```js +import { AppRegistry } from "react-native"; +import { name as appName } from "./app.json"; +import crypto from "react-native-quick-crypto"; + +import messaging from "@react-native-firebase/messaging"; +import notifee, { + AndroidImportance, + AndroidVisibility, + EventType, +} from "@notifee/react-native"; +import { NotifyClient } from "@walletconnect/notify-client"; +import { decryptMessage } from "@walletconnect/notify-message-decrypter"; + +import App from "./src/App"; + +const polyfillDigest = async (algorithm, data) => { + const algo = algorithm.replace("-", "").toLowerCase(); + const hash = crypto.createHash(algo); + hash.update(data); + return hash.digest(); +}; + +globalThis.crypto = crypto; +globalThis.crypto.subtle = { + digest: polyfillDigest, +}; + +// Create notification channel (Android only feature) +notifee.createChannel({ + id: "default", + name: "Default Channel", + lights: false, + vibration: true, + importance: AndroidImportance.HIGH, + visibility: AndroidVisibility.PUBLIC, +}); + +let notifyClient; + +const projectId = process.env.ENV_PROJECT_ID; + +async function registerAppWithFCM() { + // This is expected to be automatically handled on iOS. See https://rnfirebase.io/reference/messaging#registerDeviceForRemoteMessages + if (Platform.OS === "android") { + await messaging().registerDeviceForRemoteMessages(); + } +} + +async function registerClient(deviceToken, clientId) { + const body = JSON.stringify({ + client_id: clientId, + token: deviceToken, + type: "fcm", + always_raw: true, + }); + + const requestOptions = { + method: "POST", + headers: { "Content-Type": "application/json" }, + body, + }; + + return fetch( + `https://echo.walletconnect.com/${projectId}/clients`, + requestOptions + ) + .then((response) => response.json()) + .then((result) => console.log(">>> registered client", result)) + .catch((error) => console.log(">>> error while registering client", error)); +} + +async function handleGetToken(token) { + const status = await messaging().requestPermission(); + const enabled = + status === messaging.AuthorizationStatus.AUTHORIZED || + status === messaging.AuthorizationStatus.PROVISIONAL; + + if (enabled) { + notifyClient = await NotifyClient.init({ projectId }); + const clientId = await notifyClient.core.crypto.getClientId(); + return registerClient(token, clientId); + } +} + +messaging().getToken().then(handleGetToken); +messaging().onTokenRefresh(handleGetToken); + +async function onMessageReceived(remoteMessage) { + if (!remoteMessage.data?.blob || !remoteMessage.data?.topic) { + console.log("Missing blob or topic on notification message."); + return; + } + + const decryptedMessage = await decryptMessage({ + topic: remoteMessage.data?.topic, + encryptedMessage: remoteMessage.data?.blob, + }); + + return notifee.displayNotification({ + title: decryptedMessage.title, + body: decryptedMessage.body, + id: "default", + android: { + channelId: "default", + importance: AndroidImportance.HIGH, + visibility: AndroidVisibility.PUBLIC, + smallIcon: "ic_launcher", // optional, defaults to 'ic_launcher'. + // pressAction is needed if you want the notification to open the app when pressed. See https://notifee.app/react-native/docs/ios/interaction#press-action + pressAction: { + id: "default", + }, + }, + }); +} + +messaging().onMessage(onMessageReceived); +messaging().setBackgroundMessageHandler(onMessageReceived); + +notifee.onBackgroundEvent(async ({ type, detail }) => { + const { notification, pressAction } = detail; + + // Check if the user pressed the "Mark as read" action + if (type === EventType.ACTION_PRESS && pressAction.id === "mark-as-read") { + // Remove the notification + await notifee.cancelNotification(notification.id); + } +}); + +function HeadlessCheck({ isHeadless }) { + if (isHeadless) { + // App has been launched in the background by iOS, ignore + return null; + } + + // Render the app component on foreground launch + return ; +} + +AppRegistry.registerComponent(appName, () => HeadlessCheck); +``` diff --git a/wallets/react-native/notifications/push.mdx b/wallets/react-native/notifications/push.mdx new file mode 100644 index 0000000..2316249 --- /dev/null +++ b/wallets/react-native/notifications/push.mdx @@ -0,0 +1,115 @@ +--- +title: Push Notifications +--- + +WalletKit provides the functionality for wallets to receive push notifications through Firebase Cloud Messaging (FCM) and Apple Push Notification Service (APNs) via the Push Server. This feature ensures that wallets are promptly notified of incoming signature requests. Each push notification contains the encrypted details of the signature request. Upon receiving the notification, it can be decrypted and presented to the developer, allowing for customization of the message according to their requirements. + +## Server setup + +For the push notifications to be forwarded to FCM or APNs, the [Push Server](https://docs.reown.com/advanced/push-server) will need to be configured with your FCM or APNs server API credentials. + +## App setup + +### Register the device token + +To enable a device for push notifications, it's essential to register the device token using `walletKit.registerDeviceToken`. This token can be obtained from either FCM or APNS, depending on the platform used. + +To receive push notifications from WalletConnect's Push Server via Firebase Cloud Messaging, you will need to setup Firebase in your project. +You can follow their documentation - [Firebase documentation](https://rnfirebase.io/messaging/usage#installation). +Once you have Firebase configured, you can obtain the device token by calling `messaging().getToken()`. This unique token is used to identify each device. + +```ts +import messaging from '@react-native-firebase/messaging' + +const token = await messaging().getToken() +``` + +The device token will be used to register for WalletConnect push notifications by calling `walletKit.registerDeviceToken` and passing the token as an argument. +The `registerDeviceToken` should be called every time the client is initialized. + +```ts +walletKit.registerDeviceToken({ + token: await messaging().getToken(), // device token + clientId: await walletKit.core.crypto.getClientId(), //your instance clientId + notificationType: 'fcm', // notification type + enableEncrypted: true // flag that enabled detailed notifications +}) +``` + +With that the base setup is complete and you can start receiving push notifications from WalletConnect's Push Server for your sessions. + +Note, that from time to time, the device token is refreshed, so you must make sure to register it again. + +```ts +import messaging from '@react-native-firebase/messaging'; + +messaging().onTokenRefresh(async token => { + await walletKit.registerDeviceToken({ + token: await messaging().getToken(), // device token + clientId: await walletKit.core.crypto.getClientId(), //your instance clientId + notificationType: 'fcm', // notification type + enableEncrypted: true // flag that enabled detailed notifications + }); +}); +``` + +### Receiving push notifications + +After the device token is registered, the next step involves setting up the notification service specific to the platform being used. This service will decrypt the incoming requests and forward them to the developer for further processing and integration. + +To receive the actual push notifications, you will need to subscribe to firebase messaging events. + +```ts +import messaging from '@react-native-firebase/messaging'; + +// emitted when the app is open and a notification is received +messaging().onMessage(async notification => { + ... +}); + +// emitted when the app is in the background or closed and a notification is received +messaging().setBackgroundMessageHandler(async notification => { + ... +}); + +``` + +Now that we have the notifications listeners setup, we can start processing the incoming notifications. + +```ts +import { WalletKit } from '@reown/walletkit'; +import messaging from '@react-native-firebase/messaging'; + +messaging().onMessage(async notification => { + // get the topic, encrypted message & tag from the notification payload + const { topic, message, tag } = notification.data; + + // decrypt the message + // note this is static method and can be called without initializing the walletKit + const decryptedMessage = await WalletKit.notifications.decryptMessage({ + topic, + encryptedMessage: message, + }); + + /* + * `decryptedMessage` is JsonRpcRequest object, with the full payload of the incoming request such as method, params, id, etc. + * You can use it to emit local push notification with the request to the user and ask for their approval. + **/ + + /* + * the metadata contains name, description, icon and url of the dapp that initiated the request + * note that only notifications with tag `1108`(session requests) will have metadata, + **/ + let metadata + + if(tag == 1108) { + metadata = await WalletKit.notifications.getMetadata({ topic }); + } else { + // session proposals contain metadata in the request itself + metadata = decryptedMessage.params.proposer.metadata + } + + // with this information you can show a local push notification to the user + ... +}); +``` diff --git a/wallets/react-native/one-click-auth.mdx b/wallets/react-native/one-click-auth.mdx new file mode 100644 index 0000000..178d355 --- /dev/null +++ b/wallets/react-native/one-click-auth.mdx @@ -0,0 +1,137 @@ +--- +title: 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. + + + Mobile Linking Connect Flow + Mobile Linking Connect Flow + + +## 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 +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 +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 +// 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 +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. + + diff --git a/wallets/react-native/resources.mdx b/wallets/react-native/resources.mdx new file mode 100644 index 0000000..9dd36f5 --- /dev/null +++ b/wallets/react-native/resources.mdx @@ -0,0 +1,31 @@ +--- +title: 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 +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/)) diff --git a/wallets/react-native/usage.mdx b/wallets/react-native/usage.mdx new file mode 100644 index 0000000..e47bfd6 --- /dev/null +++ b/wallets/react-native/usage.mdx @@ -0,0 +1,364 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +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 and obtain a new project ID. + + + +## Initialization + + + +`@walletconnect/react-native-compat` must be installed and imported before any `@reown/*` dependencies for proper React Native polyfills. + +```ts +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 +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 +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 +// 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 +// 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 +// 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 +// ------- 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 +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 +{ + //... + 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 +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 +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 +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 +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 +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 +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 +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 +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 +await walletKit.emitSessionEvent({ + topic, + event: { + name: "chainChanged", + data: 1, + }, + chainId: "eip155:1", +}); +``` diff --git a/wallets/react-native/verify.mdx b/wallets/react-native/verify.mdx new file mode 100644 index 0000000..3fd7c16 --- /dev/null +++ b/wallets/react-native/verify.mdx @@ -0,0 +1,72 @@ +--- +title: 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. + +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 +... +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 diff --git a/wallets/walletguide/chain-list.mdx b/wallets/walletguide/chain-list.mdx new file mode 100644 index 0000000..d623181 --- /dev/null +++ b/wallets/walletguide/chain-list.mdx @@ -0,0 +1,17 @@ +--- +title: Supported Chains +--- + +import { ChainList } from "/snippets/chainlist.mdx" + +## 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 + + diff --git a/wallets/walletguide/explorer-api.mdx b/wallets/walletguide/explorer-api.mdx new file mode 100644 index 0000000..32036cc --- /dev/null +++ b/wallets/walletguide/explorer-api.mdx @@ -0,0 +1,113 @@ +--- +title: Explorer API +--- + +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` diff --git a/wallets/walletguide/submit-chain.mdx b/wallets/walletguide/submit-chain.mdx new file mode 100644 index 0000000..ab7a68c --- /dev/null +++ b/wallets/walletguide/submit-chain.mdx @@ -0,0 +1,48 @@ +--- +title: "Chain Onboarding" +sidebarTitle: "Submit New Chain" +--- + +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](/wallets/walletguide/explorer-api) + +**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) diff --git a/wallets/walletguide/submit-wallet.mdx b/wallets/walletguide/submit-wallet.mdx new file mode 100644 index 0000000..7da2e46 --- /dev/null +++ b/wallets/walletguide/submit-wallet.mdx @@ -0,0 +1,184 @@ +--- +title: WalletGuide Submission +sidebarTitle: Submit New Wallet +--- + + + +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) diff --git a/wallets/walletguide/wallet-list.mdx b/wallets/walletguide/wallet-list.mdx new file mode 100644 index 0000000..f273c67 --- /dev/null +++ b/wallets/walletguide/wallet-list.mdx @@ -0,0 +1,17 @@ +--- +title: Wallets +sidebarTitle: Wallet List +--- + +import { WalletList } from "/snippets/walletlist.mdx" + +## 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 + + \ No newline at end of file diff --git a/wallets/web/best-practices.mdx b/wallets/web/best-practices.mdx new file mode 100644 index 0000000..39500d6 --- /dev/null +++ b/wallets/web/best-practices.mdx @@ -0,0 +1,202 @@ +--- +title: 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 +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 +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 + try { + await walletKit.approveSession(params); + // update UI -> remove the loader + } catch (error) { + // present error to the user + } +``` + +Rejecting session + +```typescript + 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 +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 + 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 +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 +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 + + + ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/connection_state.gif) + diff --git a/wallets/web/chain-abstraction.mdx b/wallets/web/chain-abstraction.mdx new file mode 100644 index 0000000..3ed6768 --- /dev/null +++ b/wallets/web/chain-abstraction.mdx @@ -0,0 +1,92 @@ +--- +title: "Chain Abstraction" +--- + +import HowItWorks from "/snippets/walletkit/shared/chain-abstraction/intro.mdx"; +import ErrorHandling from "/snippets/walletkit/shared/chain-abstraction/error-handling.mdx"; + + + +## 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 +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 +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 +// 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. + + + +## Testing + +To test Chain Abstraction, you can use the [AppKit laboratory](https://appkit-lab.reown.com/library/wagmi/) and try sending [USDC/USDT](/wallets/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction supported wallet. +You can also use this [sample wallet](https://react-wallet.walletconnect.com) for testing. + + \ No newline at end of file diff --git a/wallets/web/cloud/analytics.mdx b/wallets/web/cloud/analytics.mdx new file mode 100644 index 0000000..f78ba0b --- /dev/null +++ b/wallets/web/cloud/analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +--- + +import Analytics from "/snippets/cloud/analytics.mdx"; + + diff --git a/wallets/web/cloud/explorer-submission.mdx b/wallets/web/cloud/explorer-submission.mdx new file mode 100644 index 0000000..e5f11c8 --- /dev/null +++ b/wallets/web/cloud/explorer-submission.mdx @@ -0,0 +1,7 @@ +--- +title: Explorer Submission +--- + +import ExplorerSubmission from "/snippets/cloud/explorer-submission.mdx"; + + diff --git a/wallets/web/cloud/relay.mdx b/wallets/web/cloud/relay.mdx new file mode 100644 index 0000000..5f9e1c0 --- /dev/null +++ b/wallets/web/cloud/relay.mdx @@ -0,0 +1,7 @@ +--- +title: Relay +--- + +import Relay from "/snippets/cloud/relay.mdx"; + + diff --git a/wallets/web/eip5792.mdx b/wallets/web/eip5792.mdx new file mode 100644 index 0000000..a09b8bf --- /dev/null +++ b/wallets/web/eip5792.mdx @@ -0,0 +1,284 @@ +--- +title: Wallet Call API +--- + +WalletConnect supports [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability), which defines new JSON-RPC methods that enable apps to ask a wallet to process a batch of onchain write calls and to check on the status of those calls. +Applications can specify that these onchain calls be executed taking advantage of specific capabilities previously expressed by the wallet; an additional, a novel wallet RPC is defined to enable apps to query the wallet for those capabilities. + +- `wallet_sendCalls`: Requests that a wallet submits a batch of calls. +- `wallet_getCallsStatus`: Returns the status of a call batch that was sent via wallet_sendCalls. +- `wallet_showCallsStatus`: Requests that a wallet shows information about a given call bundle that was sent with wallet_sendCalls. +- `wallet_getCapabilities`: This RPC allows an application to request capabilities from a wallet (e.g. batch transactions, paymaster communication). + +## Usage + + + + ## 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 +"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 +"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 +"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 + { + "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 + { + "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 + { + "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 + { + "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 + { + "id": "...", + "capabilities": { + "caip345": { + "caip2": "eip155:1", + "transactionHashes": ["..."], + } + } + } + ``` + + Specify the `scopedProperties` when approving a session: + + ```json + "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 + { + "receipts": [/* single receipt or array of receipts */], + "atomic": true + } + ``` + + #### For Non-Atomic Execution + ```json + { + "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 +- CAIP-25 namespaces: https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md diff --git a/wallets/web/installation.mdx b/wallets/web/installation.mdx new file mode 100644 index 0000000..a0ee763 --- /dev/null +++ b/wallets/web/installation.mdx @@ -0,0 +1,23 @@ +--- +title: Installation +--- + +Install Wallet SDK using npm or yarn. + + +```bash npm +npm install @reown/walletkit @walletconnect/utils @walletconnect/core +``` +```bash Yarn +yarn add @reown/walletkit @walletconnect/utils @walletconnect/core +``` +```bash Bun +bun add @reown/walletkit @walletconnect/utils @walletconnect/core +``` +```bash pnpm +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. diff --git a/wallets/web/one-click-auth-siws.mdx b/wallets/web/one-click-auth-siws.mdx new file mode 100644 index 0000000..bda6500 --- /dev/null +++ b/wallets/web/one-click-auth-siws.mdx @@ -0,0 +1,132 @@ +--- +title: One-click Auth / SIWS +--- + +## 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 Solana](https://github.com/phantom/sign-in-with-solana) (SIWS) 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 SIWS 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 SIWS 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 Solana 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 +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 +import { populateAuthPayload } from "@walletconnect/utils"; + +// Solana chains that your wallet supports +const supportedChains = [ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ" ] +// Solana methods that your wallet supports +const supportedMethods = ["solana_signMessage", "solana_signTransaction"]; +// 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 = `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:8nJ694gNrHx76L2eyJzQw7JBGRoW8Fdtrxf588pEqyYh`; +// 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 SIWS (Sign-In with Solana) message for each chain and account. However, at a minimum, one SIWS 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 +// 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: "caip122", + 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: "caip122", // 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 +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/solana-siws/) to test and verify that your wallet supports One-click Auth properly. + + diff --git a/wallets/web/one-click-auth.mdx b/wallets/web/one-click-auth.mdx new file mode 100644 index 0000000..0dc6f91 --- /dev/null +++ b/wallets/web/one-click-auth.mdx @@ -0,0 +1,135 @@ +--- +title: 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. + +![](/images/authenticatedSessions-light.png) + + +## 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 +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 +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 +// 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 +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. + + diff --git a/wallets/web/resources.mdx b/wallets/web/resources.mdx new file mode 100644 index 0000000..b5fe858 --- /dev/null +++ b/wallets/web/resources.mdx @@ -0,0 +1,31 @@ +--- +title: 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/)) diff --git a/wallets/web/usage.mdx b/wallets/web/usage.mdx new file mode 100644 index 0000000..6a0ef26 --- /dev/null +++ b/wallets/web/usage.mdx @@ -0,0 +1,624 @@ +--- +title: Usage +--- + +import CloudBanner from "/snippets/cloud-banner.mdx"; + +## 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. + + + Web Wallet in Reown AppKit + + Web Wallet in WalletConnect + + +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. + + +