# Pairing API
Source: https://docs.reown.com/advanced/api/core/pairing
The Pairing API is a lightweight API for establishing an encrypted, protocol-agnostic communication layer between peers.
Its purpose is to provide a secure channel for proposing protocols or sending requests between dapp and wallet.
**Don't have a project ID?**
Head over to Reown Cloud and create a new project now!
## Installation
WalletConnect currently offers Sign and Auth SDKs.
To allow a reusable communication channel between peers,
the Pairing API exposes a standard interface and allows for sending and receiving multi-protocol requests over a single pairing.
Each SDK uses the same implementation of `core/pairing` (via `@walletconnect/core`) to manage pairings.
To run multiple SDKs side-by-side (e.g. Sign and Auth), please refer to the \[Sharing a Core instance] guide.
#### Add SDK for your project.
You can add a WalletConnect Core SDKs to your project with Swift Package Manager. In order to do that:
1. Open XCode
2. Go to File -> Add Packages
3. Paste the repo GitHub URL: [https://github.com/reown-com/reown-swift](https://github.com/reown-com/reown-swift)
4. Tap Add Package
5. Select WalletConnectPairing check mark
Kotlin implementation of Android CoreClient for all WalletConnect SDKs. This SDK is developed in Kotlin and usable in both Java and Kotlin files.

#### Requirements
* Android min SDK 23
* Java 11
#### Installation
root/build.gradle.kts:
```gradle
allprojects {
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
}
}
```
app/build.gradle
```gradle
implementation("com.walletconnect:android-core:release_version")
```
#### Project set up
To use initialize RelayClient properly you will need a projectId. Go to [https://cloud.reown.com/app](https://cloud.reown.com/app), register your project and get projectId.
#### CoreClient initialization
Before using any of the WalletConnect Kotlin SDKs, it is necessary to initialize the CoreClient. The initialization of CoreClient must always happen in the Android Application class. Provide the projectId generated in the Reown Cloud, the WebSocket URL, choose the connection type, and pass the application class. You can also pass your own Relay instance using the `RelayConnectionInterface`.
```kotlin
val projectId = "" //Get Project ID at https://cloud.reown.com/
val connectionType = ConnectionType.AUTOMATIC or ConnectionType.MANUAL
val application = //Android Application level class
[Optional] val optionalRelay: RelayConnectionInterface? = /*implement interface*/
CoreClient.initialize(projectId = projectId, connectionType = connectionType, application = application, relay = optionalRelay)
```
#### Using your own Relay instance
The CoreClient offers the ability to use a custom Relay client. Just creating an instance of `RelayConnectionInterface` and passing it to `CoreClient.initialize`.
```kotlin
...
val optionalRelay: RelayConnectionInterface = /*implement interface*/
CoreClient.initialize(projectId = projectId, connectionType = connectionType, application = application, relay = optionalRelay)
```
WalletConnect currently offers Sign and Auth SDKs.
To allow a reusable communication channel between peers,
the Pairing API exposes a standard interface and allows for sending and receiving multi-protocol requests over a single pairing.
Each SDK uses the same implementation of `core/pairing` (via `@walletconnect/core`) to manage pairings.
To run multiple SDKs side-by-side (e.g. Sign and Auth), please refer to the \[Sharing a Core instance] guide.
Install the `WalletConnect.Core` nuget package, which implements the Pairing API
```shell
dotnet add package WalletConnect.Core
```
Once the `WalletConnect.Core` library is installed, create a Metadata object. It will describe your application and define its appearance in a web browser. Then configure the Pair instance with a metadata object you have instantiated.
```csharp
var metadata = new Metadata()
{
Name = "my-app",
Description = "My app description",
Icons = new[] { "https://walletconnect.com/meta/favicon.ico" },
Url = "https://walletconnect.com",
}
var options = new CoreOptions()
{
ProjectId = "...",
Name = "my-app",
}
var core = new WalletConnectCore(options);
core.Pairing.Configure(metadata);
```
Since `WalletConnectUnity` is a wrapper around `WalletConnectSharp`, usage of the pairing API is identical to `.NET`. Please refer to .NET documentation on how to use Pairing inside `WalletConnectUnity`.
#### Package Installation
To install packages via OpenUPM, you need to have [Node.js](https://nodejs.org/en/) and [openupm-cli](https://openupm.com/docs/getting-started.html#installing-openupm-cli) installed. Once you have them installed, you can run the following commands:
```bash
openupm add com.walletconnect.core
```
1. Open `Advanced Project Settings` from the gear ⚙ menu located at the top right of the Package Manager’s toolbar
2. Add a new scoped registry with the following details:
* Name: `OpenUPM`
* URL: `https://package.openupm.com`
* Scope(s): `com.walletconnect`
3. Press plus ➕ and then `Save` buttons
4. In the Package Manager windows open the add ➕ menu from the toolbar
5. Select `Add package by name...`
6. Enter the package name:
* `com.walletconnect.core`
7. Press `Add` button
1. Open the add ➕ menu in the Package Manager’s toolbar
2. Select `Add package from git URL...`
3. Enter the package URL:
**WalletConnectUnity Core**
```
https://github.com/WalletConnect/WalletConnectUnity.git?path=Packages/com.walletconnect.core
```
4. Press `Add` button
It's possible to lock the version of the package by adding `#{version}` at the end of the git URL, where `#{version}` is the git tag of the version you want to use.
For example, to install version `1.0.1` of WalletConnectUnity Modal, use the following URL:
```
https://github.com/WalletConnect/WalletConnectUnity.git?path=Packages/com.walletconnect.core#core/1.0.1
```
#### WebGL
Due to WebGL's single-threaded nature, certain asynchronous operations like `Task.Run`, `Task.ContinueWith`, `Task.Delay`, and `ConfigureAwait(false)` are not natively supported.
To enable these operations in WebGL builds, an additional third-party package, [WebGLThreadingPatcher](https://github.com/VolodymyrBS/WebGLThreadingPatcher), is required. This package modifies the Unity WebGL build to delegate work to the `SynchronizationContext`, allowing these operations to be executed on the same thread without blocking the main application. Please note that all tasks are still executed on a single thread, and any blocking calls will freeze the entire application.
The [WebGLThreadingPatcher](https://github.com/VolodymyrBS/WebGLThreadingPatcher) package can be added via git URL:
```
https://github.com/VolodymyrBS/WebGLThreadingPatcher.git
```
#### Initialization
1. Fill in the Project ID and Metadata fields in the `Assets/WalletConnectUnity/Resources/WalletConnectProjectConfig` asset.
* If you don’t have a Project ID, you can create one at [Reown Cloud](https://cloud.reown.com).
* The `Redirect` fields are optional. They are used to redirect the user back to your app after they approve or reject the session.
2. Initialize `WalletConnect` and get reference to the instance of `Core`:
```csharp
// Initialize singleton
await WalletConnect.Instance.InitializeAsync();
// Or handle instancing manually
var walletConnectUnity = new WalletConnect();
await walletConnectUnity.InitializeAsync();
var core = WalletConnect.Instance.SignClient.Core;
```
## Usage
The methods listed below are limited to only the public methods of the Pairing API that we recommend you interact with directly.
For an exhaustive list, please refer to the spec and/or implementation linked under [Useful Links](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/pairing-methods) above.
The keyword `sdkClient` is used here as a placeholder for any WalletConnect SDK that implements the Pairing API (e.g. `signClient`, `authClient`, etc).
```ts
// Creates a new (inactive) pairing. Returns the URI for a peer to consume via `pair`, as well as the pairing topic.
const {topic, uri} = await sdkClient.core.pairing.create()
// Pair with a peer's proposed pairing, extracted from the provided `uri` parameter.
await sdkClient.core.pairing.pair({ uri: "wc:1b3eda3f4..." })
// Activate a previously created pairing (e.g. after the peer has paired), by providing the pairing topic.
await sdkClient.core.pairing.activate({ topic: "1b3eda3f4..." })
// Updates the expiry of an existing pairing, by providing the pairing topic and an `expiry` in seconds (e.g. `60` for one minute from now)
await sdkClient.core.pairing.updateExpiry({ topic: "1b3eda3f4...", expiry: 60 })
// Updates a pairing's metadata, by providing the pairing topic and the desired metadata.
await sdkClient.core.pairing.updateMetadata({ topic: "1b3eda3f4...", metadata: { name: "MyDapp", ... } })
// Returns an array of all existing pairings.
const pairings = sdkClient.core.pairing.getPairings()
// Pings a pairing's peer, by providing the pairing topic.
await sdkClient.core.pairing.ping({ topic: "1b3eda3f4..." })
// Disconnects/Removes a pairing, by providing the pairing topic.
await sdkClient.core.pairing.disconnect({ topic: "1b3eda3f4..." })
```
#### Listeners for pairing-related events
The Pairing API currently emits the following events:
* `pairing_ping`
* `pairing_delete`
* `pairing_expire`
Any of these events can be listened for via the standard Node [`EventEmitter` interface](https://nodejs.org/api/events.html#class-eventemitter):
```ts
sdkClient.core.pairing.events.on("pairing_delete", ({ id, topic }) => {
// clean up after the pairing for `topic` was deleted.
});
```
Create an AppMetadata object. It will describe your application and define its appearance in a web browser.
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.
Then configure the Pair instance with a metadata object you have instantiated.
```swift
let metadata = AppMetadata(name: ,
description: ,
url: ,
icons: <[String]>,
redirect: AppMetadata.Redirect(native: "example://", universal: nil))
Pair.configure(metadata: metadata)
```
#### Pairing Wallet Usage
In pair wallet with dapp, the user needs to scan a QR code or open a deep link generated by dapp, then instantiate `WalletConnectURI` from the scanned QR code string and call the `pair()` function as follows.
```swift
let uri WalletConnectURI(string: )
try! await Pair.instance.pair(uri: uri)
```
Now wallet and a dapp have a secure communication channel that will be used by top level APIs.
#### Pairing Dapp Usage
In order to pair dapp and a wallet, dapp needs to generate and share a uri with wallet.
To generate a uri call `create()` function on Pair instance as follows.
```swift
let uri = try await Pair.instance.create()
```
Now you can share the uri with the wallet.
#### **Create Pairing**
```kotlin
val pairing: Pairing? = CoreClient.Pairing.create() { error -> }
```
When first establishing a pairing with a Peer, call `CoreClient.Pairing.create`. This will try and generate a new pairing with a URI parameter that can be used to establish a connection with the other Peer as well as other meta data related to the pairing.
#
#### **Pair Clients**
```kotlin
val pairingParams = Core.Params.Pair(pairingUri)
CoreClient.Pairing.pair(pairingParams) { error -> }
```
To pair the wallet with the Dapp, call the CoreClient.Pairing's pair function which needs a `Core.Params.Pair` parameter. `Core.Params.Pair` is where the WC Uri will be passed.
#
#### **Get List of Active Pairings**
```kotlin
val listOfActivePairings: List = CoreClient.Pairing.getPairings()
```
To get a list of the most current active pairings, call `CoreClient.Pairing.getPairings()` which will return a list of type `Core.Model.Pairing`.
#
#### **Disconnect a Pairing**
```kotlin
CoreClient.Pairing.disconnect(topic = /*Pairing topic*/") { error -> }
```
To disconnect from a pairing, just pass the topic of the pairing to disconnect from (use `getPairings()` to get a list of all active pairings and their topics).
The methods listed below are limited to only the public methods of the Pairing API that we recommend you interact with directly.
For an exhaustive list, please refer to the spec and/or implementation linked under [Useful Links](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/pairing-methods) above.
The keyword `sdkClient` is used here as a placeholder for any WalletConnect SDK that implements the Pairing API (e.g. `signClient`, `authClient`, etc).
```ts
// Creates a new (inactive) pairing. Returns the URI for a peer to consume via `pair`, as well as the pairing topic.
const {topic, uri} = await sdkClient.core.pairing.create()
// Pair with a peer's proposed pairing, extracted from the provided `uri` parameter.
await sdkClient.core.pairing.pair({ uri: "wc:1b3eda3f4..." })
// Activate a previously created pairing (e.g. after the peer has paired), by providing the pairing topic.
await sdkClient.core.pairing.activate({ topic: "1b3eda3f4..." })
// Updates the expiry of an existing pairing, by providing the pairing topic and an `expiry` in seconds (e.g. `60` for one minute from now)
await sdkClient.core.pairing.updateExpiry({ topic: "1b3eda3f4...", expiry: 60 })
// Updates a pairing's metadata, by providing the pairing topic and the desired metadata.
await sdkClient.core.pairing.updateMetadata({ topic: "1b3eda3f4...", metadata: { name: "MyDapp", ... } })
// Returns an array of all existing pairings.
const pairings = sdkClient.core.pairing.getPairings()
// Pings a pairing's peer, by providing the pairing topic.
await sdkClient.core.pairing.ping({ topic: "1b3eda3f4..." })
// Disconnects/Removes a pairing, by providing the pairing topic.
await sdkClient.core.pairing.disconnect({ topic: "1b3eda3f4..." })
```
#### Listeners for pairing-related events
The Pairing API currently emits the following events:
* `pairing_ping`
* `pairing_delete`
* `pairing_expire`
Any of these events can be listened for via the standard Node [`EventEmitter` interface](https://nodejs.org/api/events.html#class-eventemitter):
```ts
sdkClient.core.pairing.events.on("pairing_delete", ({ id, topic }) => {
// clean up after the pairing for `topic` was deleted.
});
```
#### Pairing Wallet Usage
When paring a wallet with a dapp, the user needs to scan a QR code or open a deep link generated by the dapp. Grab the string from the scanned QR code string or from the deep link and call the `Pair()` function as follows.
```csharp
var uri = "...";
PairingStruct pairingData = await core.Pairing.Pair(uri);
```
Now the wallet and a dapp have a secure communication channel that will be used by top level APIs.
#### Pairing Dapp Usage
In order to pair dapp and a wallet, dapp needs to generate and share a uri with wallet. To generate a uri call `create()` function on Pair instance as follows.
```csharp
var pairData = await core.Pairing.Create();
string topic = pairData.Topic;
string uri = pairData.Uri;
```
Now you can share the uri with the wallet either through a QR Code or by using a deep link.
#### Message Sending / Handling
Once a wallet and dapp has been paired, they can send messages securely to the pairing topic.
Requests can be received from the dapp by handling the message callback in the `MessageHandler` module.
```csharp
core.MessageHandler.MessageEventHandler()
.FilterRequests(r => r.Topic == pairingData.Topic)
.OnRequest +=
async delegate(RequestEventArgs eventArgs)
{
Console.WriteLine(eventArgs.Request);
eventArgs.Response = new MyResponse()
{
// ...
};
};
```
A response can be sent for any request by setting the `Response` field in the `eventArgs` parameter.
Receiving responses is handled the same way, but instead of the `OnRequest` event you would use the `OnResponse` event.
Request, Responses and Errors can be sent using the `SendRequest` , `SendResult` and `SendError` functions in the `MessageHandler` module.
```csharp
long id = await core.MessageHandler.SendRequest(pairingTopic, data);
```
# Relay Client
Source: https://docs.reown.com/advanced/api/core/relay
Relay client provides transport layer for Sign, Auth and Chat SDKs.
You can configure it once and every SDK will transport protocol messages via same instance of a relay client with only one opened websocket connection.
The Relay API can be accessed through the Core Client
Before using Sign or Auth SDK, it is necessary to configure a shared Networking Client instance. Set a project ID generated when starting a project on Reown Cloud and SocketFactory instance.
WalletConnect Swift SDK does not depend on any websocket library. SocketFactory parameter allows you to pass your own implementation of websocket connection.
Here's an example of WebSocketFactory implementation using Starscream v3
```swift
import Starscream
extension WebSocket: WebSocketConnecting { }
struct SocketFactory: WebSocketFactory {
func create(with url: URL) -> WebSocketConnecting {
return WebSocket(url: url)
}
}
```
Please note that if you have made changes to the list of **Allowed Domains** in the **Reown Cloud Dashboard**, then you may encounter an error with the connection if you use **Starscream** or any other socket client. For example, the native implementation of **Starscream** will use the `relay.walletconnect.com` as an `Origin` parameter if not provided, which will be missing from the list of **Allowed Domains**. The solution to this could be the inclusion of the `relay.walletconnect.com` in the **Allowed Domains**, corresponding changes in the socket client implementation, or following changes in the `WebSocketFactory`.
Create and register App Group Identifier in [Apple Developer Center](https://developer.apple.com/account/resources/identifiers/list/applicationGroup) if needed and provide it during Networking client configuration.
```swift
import Starscream
extension WebSocket: WebSocketConnecting { }
struct DefaultSocketFactory: WebSocketFactory {
func create(with url: URL) -> WebSocketConnecting {
var urlRequest = URLRequest(url: url)
urlRequest.addValue("allowed.domain.com", forHTTPHeaderField: "Origin")
return WebSocket(request: urlRequest)
}
}
```
#### Networking client configuration
```swift
Networking.configure(groupIdentifier: , projectId: , socketFactory: SocketFactory())
```
`groupIdentifier` - App group identifier, created on [Apple Developer Center](https://developer.apple.com/account/resources/identifiers/list/applicationGrou). Enables to share keychain items between the Notify SDK and a UNNotificationServiceExtension to receive and decrypt push notifications.
#### Web Socket Connection
By default web socket connection is handled internally by the SDK. That means that Web socket will be safely disconnected when apps go to background and it will connect back when app reaches foreground. But if it is not expected for your app and you want to handle socket connection manually you can do it as follows:
1. set socketConnectionType for manual
```swift
Networking.configure(projectId: , socketFactory: SocketFactory(), socketConnectionType: .manual)
```
2. control socket connection:
```swift
try Networking.instance.connect()
```
```swift
try Networking.instance.disconnect()
```
#### Web Socket connection control
There are two connection types, Automatic and Manual.
Automatic connection type enables SDK to control web socket connection internally. Meaning, web socket connection is closed when app goes to the background and is opened when app goes to the foreground.
Manual connection type enables developers to control web socket connection.
```kotlin
CoreClient.initialize(projectId = projectId, connectionType = ConnectionType.MANUAL, application = application)
CoreClient.Relay.connect() { error -> /*Error when wrong connection type is in use*/}
CoreClient.Relay.disconnect() { error -> /*Error when wrong connection type is in use*/}
```
# Shared Core Instance
Source: https://docs.reown.com/advanced/api/core/shared-core
The following content are only available for JavaScript.
WalletConnect's SDKs are designed to share common logic and resources via the `@walletconnect/core` package.
**If you intend to leverage multiple SDKs together (e.g. Sign + Auth), it is highly recommended to instantiate
a single `Core` instance and pass it to the relevant SDKs.** This avoids each SDK creating its own `Core` instance,
and thus duplicating computation, memory allocation, event listeners etc.
In the following example, we first instantiate a `Core` instance, and then proceed to instantiate both the Sign
and Auth SDK with this shared `Core`:
```ts
import { Core } from "@walletconnect/core";
import SignClient from "@walletconnect/sign-client";
import { AuthClient } from "@walletconnect/auth-client";
// First instantiate a separate `Core` instance.
const core = new Core({
projectId: "",
});
const metadata = {
name: "Example Dapp",
description: "Example Dapp",
url: "#",
icons: ["https://walletconnect.com/walletconnect-logo.png"],
};
// Pass `core` to the SignClient on init.
const signClient = await SignClient.init({ core, metadata });
// Pass `core` to the AuthClient on init.
const authClient = await AuthClient.init({ core, metadata });
```
# Introduction
Source: https://docs.reown.com/advanced/api/notify/about
WalletConnect Notify is a push notification protocol that enables apps to notify users of both off-chain and on-chain events.
The Notify API allows wallet users to register subscriptions for different on-chain or off-chain events that are relevant to the user.
The notifications are sent to the respective wallets.
To integrate on the Dapp Side, check [here.](/appkit/features/notifications)
To integrate on the Wallet Side, check [here.](/walletkit/features/notifications)
# Dapp Usage
Source: https://docs.reown.com/advanced/api/sign/dapp-usage
## Implementation
This library is compatible with Node.js, browsers and React Native applications (Node.js modules require polyfills for React Native).
Dapps will also need to install WalletConnectModal for the UI.
`bash npm npm install @walletconnect/modal ` `bash Yarn yarn add
@walletconnect/modal ` `bash Bun bun add @walletconnect/modal ` `bash
pnpm pnpm add @walletconnect/modal `
For an example implementation, please refer to our `react-dapp-v2`
[example](https://github.com/WalletConnect/web-examples/tree/main/advanced/dapps/react-dapp-v2).
#### Install Packages
Dapps will also need to install `WalletConnectModal` for the UI.
`bash npm npm install @walletconnect/modal ` `bash Yarn yarn add
@walletconnect/modal ` `bash Bun bun add @walletconnect/modal ` `bash
pnpm pnpm add @walletconnect/modal `
#### Create a Session
**1. Initiate your WalletConnect client with the relay server, using [your Project ID](../../cloud/relay).**
```javascript
import SignClient from "@walletconnect/sign-client";
const signClient = await SignClient.init({
projectId: "",
// optional parameters
relayUrl: "",
metadata: {
name: "Example Dapp",
description: "Example Dapp",
url: "#",
icons: ["https://walletconnect.com/walletconnect-logo.png"],
},
});
```
**2. Add listeners for desired `SignClient` events.**
To listen to pairing-related events, please follow the guidance for [Pairing
API event
listeners](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/pairing-api).
```javascript
signClient.on("session_event", ({ event }) => {
// Handle session events, such as "chainChanged", "accountsChanged", etc.
});
signClient.on("session_update", ({ topic, params }) => {
const { namespaces } = params;
const _session = signClient.session.get(topic);
// Overwrite the `namespaces` of the existing session with the incoming one.
const updatedSession = { ..._session, namespaces };
// Integrate the updated session state into your dapp state.
onSessionUpdate(updatedSession);
});
signClient.on("session_delete", () => {
// Session was deleted -> reset the dapp state, clean up from user session, etc.
});
```
**3. Create a new WalletConnectModal instance.**
```javascript
import { WalletConnectModal } from "@walletconnect/modal";
const walletConnectModal = new WalletConnectModal({
projectId: "",
// `standaloneChains` can also be specified when calling `walletConnectModal.openModal(...)` later on.
standaloneChains: ["eip155:1"],
});
```
**4. Connect the application and specify session permissions.**
```javascript
try {
const { uri, approval } = await signClient.connect({
// Optionally: pass a known prior pairing (e.g. from `signClient.core.pairing.getPairings()`) to skip the `uri` step.
pairingTopic: pairing?.topic,
// Provide the namespaces and chains (e.g. `eip155` for EVM-based chains) we want to use in this session.
requiredNamespaces: {
eip155: {
methods: [
"eth_sendTransaction",
"eth_signTransaction",
"eth_sign",
"personal_sign",
"eth_signTypedData",
],
chains: ["eip155:1"],
events: ["chainChanged", "accountsChanged"],
},
},
});
// Open QRCode modal if a URI was returned (i.e. we're not connecting an existing pairing).
if (uri) {
walletConnectModal.openModal({ uri });
// Await session approval from the wallet.
const session = await approval();
// Handle the returned session (e.g. update UI to "connected" state).
// * You will need to create this function *
onSessionConnect(session);
// Close the QRCode modal in case it was open.
walletConnectModal.closeModal();
}
} catch (e) {
console.error(e);
}
```
#### Session Authenticate with ReCaps
The authenticate() method enhances the WalletConnect protocol, offering EVM dApps a sophisticated mechanism to request wallet authentication and simultaneously establish a session. This innovative approach not only authenticates the user but also facilitates a seamless session creation, integrating the capabilities defined by ERC-5573, also known as ReCaps.
ReCaps extend the SIWE protocol, enabling users to give informed consent for dApps to exercise scoped capabilities on their behalf. This consent mechanism is crucial for authorizing a dApp to perform actions or access resources, thus ensuring security and trust in dApp interactions. These scoped capabilities are specified through ReCap URIs in the resources field of the AuthRequestParams, which translate to human-readable consent in the SIWE message, detailing the actions a dApp is authorized to undertake.
To initiate an authentication and authorization request, a dApp invokes the authenticate() method, passing in parameters that include desired capabilities as outlined in EIP-5573. The method generates a pairing URI for user interaction, facilitating a streamlined authentication and consent process.
Example of initiating an authentication request with ReCaps:
```typescript
const { uri, response } = await signClient.authenticate({
chains: ['eip155:1', 'eip155:2'], // chains your dapp requests authentication for
domain: 'localhost', // your domain
uri: 'http://localhost/login', // uri
nonce: '1239812982', // random nonce
methods: ['personal_sign', 'eth_chainId', 'eth_signTypedData_v4'], // the methods you wish to use
resources: ['https://example.com'] // any resources relevant to the connection
})
// Present the URI to users as QR code to be able to connect with a wallet
...
// wait for response
const result = await response()
// after a Wallet establishes a connection response will resolve with auths ( authentication objects ) & the established session
const { auths, session } = result;
// now you can send requests to that session
```
#### Making Requests
Once the session has been established successfully, you can start making JSON-RPC requests to be approved and signed by the wallet:
```javascript
const result = await signClient.request({
topic: session.topic,
chainId: "eip155:1",
request: {
method: "personal_sign",
params: [
"0x7468697320697320612074657374206d65737361676520746f206265207369676e6564",
"0x1d85568eEAbad713fBB5293B45ea066e552A90De",
],
},
});
```
> For more information on available JSON-RPC requests, see the [JSON-RPC reference](../../advanced/multichain/rpc-reference/ethereum-rpc.md).
### Restoring a Session
Sessions are saved to localstorage, meaning that even if the web page is reloaded, the session can still be retrieved, as demonstrated in the following code:
```ts
const lastKeyIndex = signClient.session.getAll().length - 1;
const lastSession = signClient.session.getAll()[lastKeyIndex];
```
#### Finding a Specific Session
If you need to find a specific session, you can do so by passing in a known `requiredNamespace` and calling `find`.
```ts
const specificSession = _client.find({
requiredNamespaces: {
eip155: {
methods: [
"eth_sendTransaction",
"eth_signTransaction",
"eth_sign",
"personal_sign",
"eth_signTypedData",
],
chains: ["eip155:5"],
events: ["chainChanged", "accountsChanged"],
},
},
});
```
#### Configure Networking and Pair clients
Make sure that you properly configure Networking and Pair Clients first.
* [Networking](/advanced/api/core/relay)
* [Pairing](/advanced/api/core/pairing)
#### Configure Sign Client
In order to initialize a client, call a `configure` method on the Sign instance
```swift
Sign.configure(crypto: CryptoProvider)
```
#### Subscribe for Sign publishers
When your `Sign` instance receives requests from a peer it will publish related event. So you should set subscription to handle them.
To track sessions subscribe to `sessionsPublisher` publisher
```swift
Sign.instance.sessionsPublisher
.receive(on: DispatchQueue.main)
.sink { [unowned self] (sessions: [Session]) in
// reload UI
}.store(in: &publishers)
```
Following publishers are available to subscribe:
```swift
public var sessionsPublisher: AnyPublisher<[Session], Never>
public var sessionProposalPublisher: AnyPublisher
public var sessionRequestPublisher: AnyPublisher
public var socketConnectionStatusPublisher: AnyPublisher
public var sessionSettlePublisher: AnyPublisher
public var sessionDeletePublisher: AnyPublisher<(String, Reason), Never>
public var sessionResponsePublisher: AnyPublisher
public var sessionRejectionPublisher: AnyPublisher<(Session.Proposal, Reason), Never>
public var sessionUpdatePublisher: AnyPublisher<(sessionTopic: String, namespaces: [String : SessionNamespace]), Never>
public var sessionEventPublisher: AnyPublisher<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>
public var sessionUpdateExpiryPublisher: AnyPublisher<(sessionTopic: String, expiry: Date), Never>
```
#### Connect Clients
1. Prepare namespaces that constraints minimal requirements for your dApp:
```Swift
let methods: Set = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"]
let blockchains: Set = [Blockchain("eip155:1")!, Blockchain("eip155:137")!]
let namespaces: [String: ProposalNamespace] = ["eip155": ProposalNamespace(chains: blockchains, methods: methods, events: []]
```
To learn more on namespaces, check out our [specs](https://specs.walletconnect.com/2.0/specs/clients/sign/namespaces).
2. Your App should generate a pairing URI and share it with a wallet. Uri can be presented as a QR code or sent via a universal link. Wallet begins subscribing for session proposals after receiving URI. In order to create a pairing and send a session proposal, you need to call the following:
```Swift
let uri = try await Sign.instance.connect(requiredNamespaces: namespaces, topic: uri.topic)
```
#### Session Authenticate with ReCaps
The authenticate() method enhances the WalletConnect protocol, offering EVM dApps a sophisticated mechanism to request wallet authentication and simultaneously establish a session. This innovative approach not only authenticates the user but also facilitates a seamless session creation, integrating the capabilities defined by ERC-5573, also known as ReCaps.
ReCaps extend the SIWE protocol, enabling users to give informed consent for dApps to exercise scoped capabilities on their behalf. This consent mechanism is crucial for authorizing a dApp to perform actions or access resources, thus ensuring security and trust in dApp interactions. These scoped capabilities are specified through ReCap URIs in the resources field of the AuthRequestParams, which translate to human-readable consent in the SIWE message, detailing the actions a dApp is authorized to undertake.
To initiate an authentication and authorization request, a dApp invokes the authenticate() method, passing in parameters that include desired capabilities as outlined in EIP-5573. The method generates a pairing URI for user interaction, facilitating a streamlined authentication and consent process.
Example of initiating an authentication request with ReCaps:
```swift
func initiateAuthentication() {
Task {
do {
let authParams = AuthRequestParams.stub() // Customize your AuthRequestParams as needed
let uri = try await Sign.instance.authenticate(authParams)
// Present the URI to the user, e.g., show a QR code or send a deep link
presentAuthenticationURI(uri)
} catch {
print("Failed to initiate authentication request: \(error)")
}
}
}
```
##### Subscribe to Authentication Responses
Once you have initiated an authentication request, you need to listen for responses from wallets. Responses will indicate whether the authentication request was approved or rejected. Use the authResponsePublisher to subscribe to these events.
Example subscription to authentication responses:
```swift
Sign.instance.authResponsePublisher
.receive(on: DispatchQueue.main)
.sink { response in
switch response.result {
case .success(let (session, _)):
if let session = session {
// Authentication successful, session established
handleSuccessfulAuthentication(session)
} else {
// Authentication successful, but no session created (SIWE-only flow)
handleSuccessfulAuthenticationWithoutSession()
}
case .failure(let error):
// Authentication request was rejected or failed
handleAuthenticationFailure(error)
}
}
.store(in: &subscriptions)
```
In this setup, the authResponsePublisher notifies your dApp of the outcome of the authentication request. Your dApp can then proceed based on whether the authentication was successful, rejected, or failed due to an error.
Example of AuthRequestParams:
```swift
extension AuthRequestParams {
static func stub(
domain: String = "yourDappDomain.com",
chains: [String] = ["eip155:1", "eip155:137"],
nonce: String = "uniqueNonce",
uri: String = "https://yourDappDomain.com/login",
statement: String? = "I accept the Terms of Service: https://yourDappDomain.com/tos",
resources: [String]? = nil, // here your dapp may request authorization with recaps
methods: [String]? = ["personal_sign", "eth_sendTransaction"]
) -> AuthRequestParams {
return try! AuthRequestParams(
domain: domain,
chains: chains,
nonce: nonce,
uri: uri,
statement: statement,
resources: resources,
methods: methods
)
}
}
```
#### Send Request to the Wallet
Once the session has been established `sessionSettlePublisher` will publish an event. Your dApp can start requesting wallet now.
```Swift
let method = "personal_sign"
let walletAddress = "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" // This should match the connected address
let requestParams = AnyCodable(["0x4d7920656d61696c206973206a6f686e40646f652e636f6d202d2031363533333933373535313531", walletAddress])
let request = Request(topic: session.topic, method: method, params: requestParams, chainId: Blockchain(chainId)!)
try await Sign.instance.request(params: request)
```
When wallet respond `sessionResponsePublisher` will publish an event so you can verify the response.
#### 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 Sign.instance.extend(topic: session.topic)
```
Above method will extend a user's session to a week.
#### Where to go from here
* Try our [Example dApp](https://github.com/reown-com/reown-swift/tree/main/Example) that is part of [WalletConnectSwiftV2 repository](https://github.com/reown-com/reown-swift).
* Build API documentation in XCode: go to Product -> Build Documentation
#### **Initialization**
```kotlin
val projectId = "" // Get Project ID at https://cloud.reown.com/
val connectionType = ConnectionType.AUTOMATIC or ConnectionType.MANUAL
val appMetaData = Core.Model.AppMetaData(
name = "Dapp Name",
description = "Dapp Description",
url = "Dapp URL",
icons = /*list of icon url strings*/,
redirect = "kotlin-dapp-wc:/request" // Custom Redirect URI
)
CoreClient.initialize(projectId = projectId, connectionType = connectionType, application = this, metaData = appMetaData)
val init = Sign.Params.Init(core = CoreClient)
SignClient.initialize(init) { error ->
// Error will be thrown if there's an issue during initialization
}
```
The Dapp client is responsible for initiating the connection with wallets and defining the required namespaces (CAIP-2) from the Wallet and is also in charge of sending requests. To initialize the Sign client, create a `Sign.Params.Init` object in the Android Application class with the Core Client. The `Sign.Params.Init` object will then be passed to the `SignClient` initialize function.
#
# **Dapp**
#### **SignClient.DappDelegate**
```kotlin
val dappDelegate = object : SignClient.DappDelegate {
override fun onSessionApproved(approvedSession: Sign.Model.ApprovedSession) {
// Triggered when Dapp receives the session approval from wallet
}
override fun onSessionRejected(rejectedSession: Sign.Model.RejectedSession) {
// Triggered when Dapp receives the session rejection from wallet
}
fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) {
// Triggered when Dapp receives the session authenticate response from wallet
}
override fun onSessionUpdate(updatedSession: Sign.Model.UpdatedSession) {
// Triggered when Dapp receives the session update from wallet
}
override fun onSessionExtend(session: Sign.Model.Session) {
// Triggered when Dapp receives the session extend from wallet
}
override fun onSessionEvent(sessionEvent: Sign.Model.SessionEvent) {
// Triggered when the peer emits events that match the list of events agreed upon session settlement
}
override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) {
// Triggered when Dapp receives the session delete from wallet
}
override fun onSessionRequestResponse(response: Sign.Model.SessionRequestResponse) {
// Triggered when Dapp receives the session request response from wallet
}
override fun onProposalExpired(proposal: Modal.Model.ExpiredProposal) {
// Triggered when a proposal becomes expired
}
override fun onRequestExpired(request: Modal.Model.ExpiredRequest) {
// Triggered when a request becomes expired
}
override fun onConnectionStateChange(state: Sign.Model.ConnectionState) {
//Triggered whenever the connection state is changed
}
override fun onError(error: Sign.Model.Error) {
// Triggered whenever there is an issue inside the SDK
}
}
SignClient.setDappDelegate(dappDelegate)
```
The SignClient needs a `SignClient.DappDelegate` passed to it for it to be able to expose asynchronously updates sent from the Wallet.
#
#### **Connect**
```kotlin
val namespace: String = /*Namespace identifier, see for reference: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md#syntax*/
val chains: List = /*List of chains that wallet will be requested for*/
val methods: List = /*List of methods that wallet will be requested for*/
val events: List = /*List of events that wallet will be requested for*/
val requiredNamespaces: Map = mapOf(namespace, Sign.Model.Namespaces.Proposal(accounts, methods, events)) /*Required namespaces to setup a session*/
val optionalNamespaces: Map = mapOf(namespace, Sign.Model.Namespaces.Proposal(accounts, methods, events)) /*Optional namespaces to setup a session*/
val pairing: Core.Model.Pairing = /*Either an active or inactive pairing*/
val connectParams = Sign.Params.Connect(requiredNamespaces, optionalNamespaces, pairing)
fun SignClient.connect(connectParams,
{ onSuccess ->
/*callback that returns letting you know that you have successfully initiated connecting*/
},
{ error ->
/*callback for error while trying to initiate a connection with a peer*/
}
)
```
More about optional and required namespaces can be found [here](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md)
#
#### **Authenticate**
The authenticate() method enhances the WalletConnect protocol, offering EVM dApps a sophisticated mechanism to request wallet authentication and simultaneously establish a session. This innovative approach not only authenticates the user but also facilitates a seamless session creation, integrating the capabilities defined by ERC-5573, also known as ReCaps.
Capabilities are specified through ReCap URIs in the resources field of the Sign.Params.Authenticate, which translate to human-readable consent in the SIWE message, detailing the actions a dApp is authorized to undertake.
To initiate an authentication and authorization request, a dApp invokes the authenticate() method, passing in parameters that include desired capabilities as outlined in EIP-5573. The method generates a pairing URI for user interaction, facilitating a streamlined authentication and consent process.
Example of initiating an authentication request with ReCaps:
```kotlin
val authenticateParams = Sign.Params.Authenticate(
domain = "your.domain",
chains = listof("eip155:1", "eip155:137"),
methods = listOf("personal_sign", "eth_signTypedData"),
uri = "https://yourDappDomain.com/login",
nonce = randomNonce,
statement = "Sign in with wallet.",
resources = null, // here your dapp may request authorization with recaps
)
SignClient.authenticate(authenticateParams,
onSuccess = { url ->
//Handle authentication URI. Show as a QR code a send via deeplink
},
onError = { error ->
//Handle error
}
)
```
Once you have sent an authentication request, await for responses from wallets. Responses will indicate whether the authentication request was approved or rejected. Use the onSessionAuthenticateResponse callback to receive a response:
```kotlin
fun onSessionAuthenticateResponse(sessionAuthenticateResponse: Sign.Model.SessionAuthenticateResponse) {
// Triggered when Dapp receives the session authenticate response from wallet
if (sessionAuthenticateResponse is Sign.Model.SessionAuthenticateResponse.Result) {
if (sessionAuthenticateResponse.session != null) {
// Authentication successful, session established
} else {
// Authentication successful, but no session created (SIWE-only flow)
}
} else {
// Authentication request was rejected or failed
}
}
```
#
#### **Get List of Settled Sessions**
```kotlin
SignClient.getListOfSettledSessions()
```
To get a list of the most current settled sessions, call `SignClient.getListOfSettledSessions()` which will return a list of type `Session`.
#
#### **Get list of pending session requests for a topic**
```kotlin
SignClient.getPendingRequests(topic: String)
```
To get a list of pending session requests for a topic, call `SignClient.getPendingRequests()` and pass a topic which will return
a `PendingRequest` object containing requestId, method, chainIs and params for pending request.
#### Initialization
To create an instance of `SignClient`, you need to pass in the core and metadata parameters.
```dart
SignClient signClient = await SignClient.createInstance(
relayUrl: 'wss://relay.walletconnect.com', // The relay websocket URL, leave blank to use the default
projectId: '123',
metadata: PairingMetadata(
name: 'dapp (Requester)',
description: 'A dapp that can request that transactions be signed',
url: 'https://walletconnect.com',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
),
);
```
#### Connection
To connect with specific parameters and display the returned URI, use `connect` with the required namespaces.
```dart
ConnectResponse response = await signClient.connect(
requiredNamespaces: {
'eip155': RequiredNamespace(
chains: ['eip155:1'], // Ethereum chain
methods: ['eth_signTransaction'], // Requestable Methods
),
'kadena': RequiredNamespace(
chains: ['kadena:mainnet01'], // Kadena chain
methods: ['kadena_quicksign_v1'], // Requestable Methods
),
}
);
Uri? uri = response.uri;
```
You will use that URI to display a QR code or handle a deep link.
We recommend not handling deep linking yourself. If you want to deep link, then use the [walletconnect\_modal\_flutter](https://pub.dev/packages/walletconnect_modal_flutter) package.
#### Session Data
Once you've displayed the URI you can wait for the future and hide the QR code once you've received session data.
```dart
final SessionData session = await response.session.future;
```
#### Request Signatures
Once the session had been created, you can request signatures.
```dart
final signature = await signClient.request(
topic: session.topic,
chainId: 'eip155:1',
request: SessionRequestParams(
method: 'eth_signTransaction',
params: 'json serializable parameters',
),
);
```
#### Respond to Events
You can also respond to events from the wallet, like chain changed, using `onSessionEvent` and `registerEventHandler`.
```dart
signClient.onSessionEvent.subscribe((SessionEvent? session) {
// Do something with the event
});
signClient.registerEventHandler(
namespace: 'kadena',
event: 'kadena_transaction_updated',
);
```
# To Test
Run tests using `flutter test`.
Expected flutter version is: >`3.3.10`
# Useful Commands
* `flutter pub run build_runner build --delete-conflicting-outputs` - Regenerates JSON Generators
* `flutter doctor -v` - get paths of everything installed.
* `flutter pub get`
* `flutter upgrade`
* `flutter clean`
* `flutter pub cache clean`
* `flutter pub deps`
* `flutter pub run dependency_validator` - show unused dependencies and more
* `dart format lib/* -l 120`
* `flutter analyze`
#### Setup
First you must setup `SignClientOptions` which stores both the `ProjectId` and `Metadata`. You may also optionally specify the storage module to use. By default, the `FileSystemStorage` module is used if none is specified.
```csharp
var dappOptions = new SignClientOptions()
{
ProjectId = "39f3dc0a2c604ec9885799f9fc5feb7c",
Metadata = new Metadata()
{
Description = "An example dapp to showcase WalletConnectSharpv2",
Icons = new[] { "https://walletconnect.com/meta/favicon.ico" },
Name = "WalletConnectSharpv2 Dapp Example",
Url = "https://walletconnect.com"
},
// Uncomment to disable persistent storage
// Storage = new InMemoryStorage()
};
```
Then, you must setup the `ConnectOptions` which define what blockchain, RPC methods and events your dapp will use.
*C# Constructor*
```csharp
var dappConnectOptions = new ConnectOptions()
{
RequiredNamespaces = new RequiredNamespaces()
{
{
"eip155", new RequiredNamespace()
{
Methods = new[]
{
"eth_sendTransaction",
"eth_signTransaction",
"eth_sign",
"personal_sign",
"eth_signTypedData",
},
Chains = new[]
{
"eip155:1"
},
Events = new[]
{
"chainChanged",
"accountsChanged",
}
}
}
}
};
```
*Builder Functions Style*
```csharp
var dappConnectOptions1 = new ConnectOptions()
.RequireNamespace("eip155", new RequiredNamespace()
.WithMethod("eth_sendTransaction")
.WithMethod("eth_signTransaction")
.WithMethod("eth_sign")
.WithMethod("personal_sign")
.WithMethod("eth_signTypedData")
.WithChain("eip155:1")
.WithEvent("chainChanged")
.WithEvent("accountsChanged")
);
```
With both options defined, you can initialize and connect the SDK.
```csharp
var dappClient = await WalletConnectSignClient.Init(dappOptions);
var connectData = await dappClient.Connect(dappConnectOptions);
```
You can grab the `Uri` for the connection request from `connectData`.
```csharp
ExampleShowQRCode(connectData.Uri);
```
Then await connection approval using the `Approval` Task object.
```csharp
Task sessionConnectTask = connectData.Approval;
SessionStruct sessionData = await sessionConnectTask;
// or
// SessionStruct sessionData = await connectData.Approval;
```
This `Task` will return the `SessionStruct` when the session was approved, or throw an exception when the session request has either
* Timed out
* Been Rejected
#### Connected Address
To get the currently connected address, use the following function
```csharp
public class Caip25Address
{
public string Address;
public string ChainId;
}
public Caip25Address GetCurrentAddress(string chain)
{
if (string.IsNullOrWhiteSpace(chain))
return null;
var defaultNamespace = currentSession.Namespaces[chain];
if (defaultNamespace.Accounts.Length == 0)
return null;
var fullAddress = defaultNamespace.Accounts[0];
var addressParts = fullAddress.Split(":");
var address = addressParts[2];
var chainId = string.Join(':', addressParts.Take(2));
return new Caip25Address()
{
Address = address,
ChainId = chainId,
};
}
public Caip25Address GetCurrentAddress()
{
var currentSession = dappClient.Session.Get(dappClient.Session.Keys[0]);
var defaultChain = currentSession.Namespaces.Keys.FirstOrDefault();
if (string.IsNullOrWhiteSpace(defaultChain))
return null;
return GetCurrentAddress(defaultChain);
}
```
#### WalletConnect Methods
All sign methods require 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 user.
```csharp
var sessionTopic = sessionData.Topic;
```
##### Update Session
Update a session, adding/removing additional namespaces in the given topic.
```csharp
var newNamespaces = new Namespaces(...);
var request = await dappClient.UpdateSession(sessionTopic, newNamespaces);
await request.Acknowledged();
```
##### Extend Session
Extend a session's expiry time so the session remains open
```csharp
var request = await dappClient.Extend(sessionTopic);
await request.Acknowledged();
```
##### Ping
Send a ping to the session
```csharp
var request = await dappClient.Ping(sessionTopic);
await request.Acknowledged();
```
#### Session Requests
Sending session requests as a dapp requires to build the request **and** response classes that the session request `params` will be structured. C# is a statically typed language, so these types must be given whenever you do a session request (or do any querying for session requests).
Currently, **WalletConnectSharp 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 Request type
Create a class for the request and populate it with the JSON properties the request object has. For this example, we will use `eth_sendTransaction`
The `params` field for `eth_sendTransaction` has the object type
```csharp
using Newtonsoft.Json;
public class Transaction
{
public string from;
// Newtonsoft.Json attributes can be used
[JsonProperty("to")]
public string To;
[JsonProperty("gas", NullValueHandling = NullValueHandling.Ignore)]
public string Gas;
// Properties have limited support
[JsonProperty("gasPrice", NullValueHandling = NullValueHandling.Ignore)]
public string GasPrice { get; set; }
[JsonProperty("value")]
public string Value { get; set; }
[JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
public string Data { get; set; } = "0x";
}
```
[the `params` field is an array of this object](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendtransaction)
```json
params: [
{
from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
gas: "0x76c0", // 30400
gasPrice: "0x9184e72a000", // 10000000000000
value: "0x9184e72a", // 2441406250
data: "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
},
]
```
Now, let's define the actual request class we'll use in `dappClient.Request`
```csharp
[RpcMethod("eth_sendTransaction"), RpcRequestOptions(Clock.ONE_MINUTE, 99997)]
public class EthSendTransaction : List
{
public EthSendTransaction(params Transaction[] transactions) : base(transactions)
{
}
}
```
The `RpcMethod` class attributes defines the rpc method this request uses. The `RpcRequestOptions` class attributes define the expiry time and tag attached to the request. **Both of these attributes are required**
We use `List` since the `params` field for `eth_sendTransaction` is actually sent as an object array. If the `params` field was a normal object, then we could use `Transaction` or define the fields directly into this class.
##### Sending a request
The response type for `eth_sendTransaction` is a `string`, so no response type is required to be made. You only need to create a response type if the response type is a custom object.
```csharp
var wallet = GetCurrentAddress();
var result = new EthSendTransaction(new Transaction()
{
From = wallet.Address,
To = wallet.Address,
Value = "0"
});
// Returns the transaction hash or throws an error
string result = await dappClient.Request(sessionTopic, request, wallet.ChainId);
```
#### Disconnecting
To disconnect a session, use the `Disconnect` function. You may optional provide a reason for the disconnect
```csharp
await dappClient.Disconnect(sessionTopic);
// or
await dappClient.Disconnect(sessionTopic, Error.FromErrorType(ErrorType.USER_DISCONNECTED));
```
#### Subscribe to session events
```csharp
dappClient.SubscribeToSessionEvent("chainChanged", OnChainChanged);
```
WalletConnectUnity is a wrapper for WalletConnectSharp. It simplifies managing a single active session, addressing a common challenge with the original library.
#### Features of WalletConnectUnity
1. **Simplified Session Management**: WalletConnectSharp is designed to support multiple sessions, requiring developers to manually track and restore the active session. WalletConnectUnity simplifies this process by focusing on a single session, making it easier to manage session restoration.
2. **Session Restoration**: WalletConnectUnity includes methods to easily access and restore the active session from storage.
3. **Deep Linking Support**: WalletConnectUnity automatically handles deep linking for mobile and desktop wallets.
4. **QR Code Generation**: WalletConnectUnity provides a utility for generating QR codes.
#### Usage
To use WalletConnectUnity in your project:
1. Fill in the Project ID and Metadata fields in the `Assets/WalletConnectUnity/Resources/WalletConnectProjectConfig` asset.
* If you don’t have a Project ID, you can create one at [Reown Cloud](https://cloud.reown.com).
* The `Redirect` fields are optional. They are used to redirect the user back to your app after they approve or reject the session.
2. Initialize `WalletConnect` and connect the wallet:
```csharp
// Initialize singleton
await WalletConnect.Instance.InitializeAsync();
// Or handle instancing manually
var walletConnectUnity = new WalletConnect();
await walletConnectUnity.InitializeAsync();
// Try to resume the last session
var sessionResumed = await WalletConnect.Instance.TryResumeSessionAsync();
if (!sessionResumed)
{
var connectedData = await WalletConnect.Instance.ConnectAsync(connectOptions);
// Create QR code texture
var texture = WalletConnectUnity.Core.Utils.QRCode.EncodeTexture(connectedData.Uri);
// ... Display QR code texture
// Wait for wallet approval
await connectedData.Approval;
}
```
All features of WalletConnectSharp are accessible in WalletConnectUnity.
For complex scenarios, the `SignClient` can be accessed directly through `WalletConnect.SignClient`.
Refer to the `.NET` documentation for details on using the Sign API within WalletConnectUnity.
The usage of the WalletConnectSharp.Sign API remains consistent with `.NET`.
# Introduction
Source: https://docs.reown.com/advanced/api/sign/overview
WalletConnect Sign is a remote signer protocol to communicate securely between web3 wallets and dapps. The protocol establishes a remote pairing between two apps and/or devices using a Relay server to relay payloads. These payloads are symmetrically encrypted through a shared key between the two peers. The pairing is initiated by one peer displaying a QR Code or deep link with a standard WalletConnect URI and is established when the counter-party approves this pairing request.
**Don't have a project ID?**
Head over to Reown Cloud and create a new project now!
## Installation
`bash npm npm install @walletconnect/sign-client ` `bash Yarn yarn add
@walletconnect/sign-client ` `bash Bun bun add @walletconnect/sign-client
` `bash pnpm pnpm add @walletconnect/sign-client `
For Node.js, the WalletConnect SignClient additionally requires `lokijs` to manage storage internally.
`bash npm npm install --save @walletconnect/sign-client lokijs@1.x `
`bash Yarn yarn add @walletconnect/sign-client lokijs@1.x ` `bash Bun
bun add --save @walletconnect/sign-client lokijs@1.x ` `bash pnpm pnpm add
@walletconnect/sign-client lokijs@1.x `
You can add a WalletConnect SDK to your project with Swift Package Manager. In order to do that:
1. Open XCode
2. Go to File -> Add Packages
3. Paste the repo GitHub URL: [https://github.com/reown-com/reown-swift](https://github.com/reown-com/reown-swift)
4. Tap Add Package
5. Select WalletConnect check mark
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'
```
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', :git => 'https://github.com/reown-com/reown-swift.git', :tag => '1.0.5'
```
Kotlin implementation of WalletConnect v2 Sign protocol for Android applications. This SDK is developed in Kotlin and usable in both Java and Kotlin files.
* Android Core 
* Sign 
#### Requirements
* Android min SDK 23
* Java 11
#### Installation
root/build.gradle.kts:
```gradle
allprojects {
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
}
}
```
app/build.gradle.kts
```gradle
implementation("com.walletconnect:android-core:release_version")
implementation("com.walletconnect:sign:release_version")
```
Install the WalletConnect client package.
```dart
flutter pub add walletconnect_flutter_v2
```
#### Platform Specific Setup
Depending on your platform, you will have to add different permissions to get the package to work.
#### MacOS
Add the following to your `DebugProfile.entitlements` and `Release.entitlements` files so that it can connect to the WebSocket server.
```xml
com.apple.security.network.client
```
#### Install via Packages
Install the WalletConnect Sign Client package via Nuget.
```shell
dotnet add package WalletConnect.Sign
```
WalletConnectUnity.Core is a Unity package that provides a client implementation of the WalletConnect v2 protocol. It is built on top of the [WalletConnectSharp.Sign](https://github.com/WalletConnect/WalletConnectSharp) library, which provides the core functionality for the WalletConnect protocol.
#### Prerequisites
* Unity 2021.3 or above
* IL2CPP code stripping level: Minimal (or lower)
#### Package Installation
To install packages via OpenUPM, you need to have [Node.js](https://nodejs.org/en/) and [openupm-cli](https://openupm.com/docs/getting-started.html#installing-openupm-cli) installed. Once you have them installed, you can run the following commands:
```bash
openupm add com.walletconnect.core
```
1. Open `Advanced Project Settings` from the gear ⚙ menu located at the top right of the Package Manager’s toolbar
2. Add a new scoped registry with the following details:
* Name: `OpenUPM`
* URL: `https://package.openupm.com`
* Scope(s): `com.walletconnect`
3. Press plus ➕ and then `Save` buttons
4. In the Package Manager windows open the add ➕ menu from the toolbar
5. Select `Add package by name...`
6. Enter the package name:
* `com.walletconnect.core`
7. Press `Add` button
1. Open the add ➕ menu in the Package Manager’s toolbar
2. Select `Add package from git URL...`
3. Enter the package URL:
**WalletConnectUnity Core**
```
https://github.com/WalletConnect/WalletConnectUnity.git?path=Packages/com.walletconnect.core
```
4. Press `Add` button
It's possible to lock the version of the package by adding `#{version}` at the end of the git URL, where `#{version}` is the git tag of the version you want to use.
For example, to install version `1.0.1` of WalletConnectUnity Modal, use the following URL:
```
https://github.com/WalletConnect/WalletConnectUnity.git?path=Packages/com.walletconnect.core#core/1.0.1
```
#### WebGL
Due to WebGL's single-threaded nature, certain asynchronous operations like `Task.Run`, `Task.ContinueWith`, `Task.Delay`, and `ConfigureAwait(false)` are not natively supported.
To enable these operations in WebGL builds, an additional third-party package, [WebGLThreadingPatcher](https://github.com/VolodymyrBS/WebGLThreadingPatcher), is required. This package modifies the Unity WebGL build to delegate work to the `SynchronizationContext`, allowing these operations to be executed on the same thread without blocking the main application. Please note that all tasks are still executed on a single thread, and any blocking calls will freeze the entire application.
The [WebGLThreadingPatcher](https://github.com/VolodymyrBS/WebGLThreadingPatcher) package can be added via git URL:
```
https://github.com/VolodymyrBS/WebGLThreadingPatcher.git
```
# Smart Contract Wallet Usage
Source: https://docs.reown.com/advanced/api/sign/smart-contract-wallet-usage
This section is limited to just for Web/JavaScript at the present moment
Smart Contract wallets like [Argent](https://argent.gitbook.io/argent/wallet-connect-and-argent) are fully supported by the WalletConnect protocol.
However, there are some considerations to be taken when integrating WalletConnect in your dapp for Smart Contract wallets, regarding how the accounts are exposed in the session, message signatures are returned, and transactions are broadcasted.
### Accounts
When you connect your dapp to a smart contract wallet, you will receive the
**smart account address** for the wallet. This is not to be confused with the
**delegate keys** that are used to sign messages and transactions.
You can detect smart contract wallets by verifying on-chain if the exposed account address has any associated code deployed.
```javascript
import { providers, utils } from "ethers";
const provider = new providers.JsonRpcProvider(rpcUrl);
const bytecode = await provider.getCode(address);
const isSmartContract = bytecode && utils.hexStripZeros(bytecode) !== "0x";
```
```javascript
import Web3 from "web3";
const web3 = new Web3(rpcUrl);
const bytecode = await web3.eth.getCode(address);
const isSmartContract = bytecode && utils.hexStripZeros(bytecode) !== "0x";
```
Smart contract wallets are essentially multi-signature wallets that use multiple keys to authorize operations on behalf of these smart contract accounts, so you will have to take into consideration how messages and transactions are handled by your dapp.
## Messages
Normally, when verifying signatures from "normal" accounts, which are Externally Owned Accounts (EOAs), you would use an ECDSA method called `ecrecover()` to retrieve the corresponding public key, which will then map to an address.
In the case of Smart Contract Wallets, you are not able to sign a message with the smart contract account. Therefore, the standard [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) was defined to outline a validation method which can be called on-chain, labeled `isValidSignature()`.
```text
contract ERC1271 {
bytes4 constant internal MAGICVALUE = 0x1626ba7e;
function isValidSignature(
bytes32 _hash,
bytes memory _signature
)
public
view
returns (bytes4 magicValue);
}
```
This method has a single parameter `_hash` which should be [EIP-191](https://eips.ethereum.org/EIPS/eip-191) compliant and can be computed using:
```javascript
import { utils } from "ethers";
const hash = utils.hashMessage(message);
```
```javascript
import Web3 from "web3";
const web3 = new Web3(rpcUrl);
const hash = web3.eth.accounts.hashMessage(message);
```
## Transactions
Smart Contract wallets, like [Argent](https://argent.gitbook.io/argent/wallet-connect-and-argent), commonly use the concept of meta transactions. These are a specific type of transaction that is signed by one or more key pairs but is submitted to the Ethereum network by a relayer.
The relayer pays the gas fee (in ETH), and the wallet will refund the relayer (in ETH or ERC20 tokens) up to an amount signed by the wallet's owner.
From your dapp's perspective, this is managed by the mobile wallet application. Your dapp will submit a regular `{ to, value, data }` transaction to the web3 provider. This transaction will be transmitted to the mobile wallet application through WalletConnect.
The mobile wallet will transform the data into a meta transaction:
* `to` will be the `RelayerManager` contract address
* `data` will be the encoded data of the call to the `execute()` method with the relevant parameters
Your dapp will receive the transaction hash in order to monitor the status of the transaction, and events will be emitted as usual.
The relayer has the ability to replay a transaction with a higher gas price due to fluctuating network conditions. The transaction hash is modified, and the dapp will not be aware of the new transaction hash.
One solution could be for your dapp to observe a specific event being emitted instead of the transaction status. There is currently work on standardizing events for transactions replies that has been recently proposed via [EIP-2831](https://eips.ethereum.org/EIPS/eip-2831). We hope to improve our SDKs in the future to take this standard into account.
# Wallet Usage
Source: https://docs.reown.com/advanced/api/sign/wallet-usage
Sign API establishes a session between a wallet and a dapp in order to expose a set of blockchain accounts that can sign transactions or messages using a secure remote JSON-RPC transport with methods and events.
**Don't have a project ID?**
Head over to Reown Cloud and create a new project now!
This library is compatible with Node.js, browsers and React Native
applications (Node.js modules require polyfills for React Native).
#### Migrating from v1.x
**We recommend you install v1 and v2 together for maximum compatibility.** If your wallet already uses `@walletconnect/client@1.x.x`,
you should be able to add `@walletconnect/sign-client@2.x.x` without any issues.
If you experience dependency clashes or you require both `@walletconnect/types@1.x.x` and `@walletconnect/types@2.x.x` in parallel
in your wallet's top-level dependencies, please refer to the [`legacy` packages](https://github.com/WalletConnect/walletconnect-legacy/tree/main/packages) which were published explicitly for this purpose.
In the above scenario, you would replace `@walletconnect/types@1.x.x` with `@walletconnect/legacy-types` and then install `@walletconnect/types@2.x.x`.
#### Initializing the client
Initialize client as a controller using [your Project ID](../../cloud/relay).
```js
const signClient = await SignClient.init({
projectId: "",
// optional parameters
relayUrl: "",
metadata: {
name: "Wallet name",
description: "A short description for your wallet",
url: "",
icons: [""],
},
});
```
#### Setting up event listeners
WalletConnect v2.0 emits events related to the current session. The listeners listed in the following code snippet represent typical
events in a session's lifecycle that you can listen for to synchronise your application accordingly.
Example: when a `session_delete` event is emitted, it makes sense to change the UI from an active session state to
an inactive/disconnected state.
**1. Add listeners for desired `SignClient` events.**
To listen to pairing-related events, please follow the guidance for [Pairing
API event listeners.](../core/pairing)
```ts
signClient.on("session_proposal", (event) => {
// Show session proposal data to the user i.e. in a modal with options to approve / reject it
interface Event {
id: number;
params: {
id: number;
expiry: number;
relays: Array<{
protocol: string;
data?: string;
}>;
proposer: {
publicKey: string;
metadata: {
name: string;
description: string;
url: string;
icons: string[];
};
};
requiredNamespaces: Record<
string,
{
chains: string[];
methods: string[];
events: string[];
}
>;
pairingTopic?: string;
};
}
});
signClient.on("session_event", (event) => {
// Handle session events, such as "chainChanged", "accountsChanged", etc.
interface Event {
id: number;
topic: string;
params: {
event: {
name: string;
data: any;
};
chainId: string;
};
}
});
signClient.on("session_request", (event) => {
// Handle session method requests, such as "eth_sign", "eth_sendTransaction", etc.
interface Event {
id: number;
topic: string;
params: {
request: {
method: string;
params: any;
};
chainId: string;
};
}
});
signClient.on("session_ping", (event) => {
// React to session ping event
interface Event {
id: number;
topic: string;
}
});
signClient.on("session_delete", (event) => {
// React to session delete event
interface Event {
id: number;
topic: string;
}
});
```
# Pairing and session permissions
#### URI
The pairing proposal between a wallet and a dapp is made using an [URI](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/). In WalletConnect v2.0 the session and pairing are decoupled from each other. This means that a URI is shared to construct a pairing proposal, and only after settling the pairing the dapp can propose a session using that pairing. In simpler words, the dapp generates an URI that can be used by the wallet for pairing.
#### Namespaces
The `namespaces` parameter is used to specify the namespaces and chains that are intended to be used in the session. The following is an example:
```js
namespaces: {
eip155: {
accounts: ["eip155:1:0x0000000000..., eip155:2:0x0000000000..."],
methods: ["personal_sign", "eth_sendTransaction"],
events: ["accountsChanged"]
},
};
```
#### Pairing with `uri`
To create a pairing proposal, simply pass the `uri` received from the dapp into the `signClient.core.pairing.pair()` function.
As of 2.0.0 (stable), calling pairing-specific methods (such as `signClient.pair()`) directly on `signClient` will continue to work, but is considered deprecated and will be removed in a future major version.
It is recommended to instead call these methods directly via the [Pairing API.](../core//pairing), e.g.: `signClient.core.pairing.pair()`.
```js
// This will trigger the `session_proposal` event
await signClient.core.pairing.pair({ uri });
// Approve session proposal, use id from session proposal event and respond with namespace(s) that satisfy dapps request and contain approved accounts
const { topic, acknowledged } = await signClient.approve({
id: 123,
namespaces: {
eip155: {
accounts: ["eip155:1:0x0000000000..."],
methods: ["personal_sign", "eth_sendTransaction"],
events: ["accountsChanged"],
},
},
});
// Optionally await acknowledgement from dapp
const session = await acknowledged();
// Or reject session proposal
await signClient.reject({
id: 123,
reason: {
code: 1,
message: "rejected",
},
});
```
#### Pairing with QR Codes
To facilitate better user experience, it is possible to pair wallets with dapps by scanning QR codes. This can be implemented by using any QR code scanning library (example, [react-qr-reader](https://www.npmjs.com/package/react-qr-reader)). After scanning the QR code, pass the obtained `uri` into the `signClient.pair()` function. A useful reference for implementing QR codes for pairing is the [react wallet example](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/).
## Authenticated Session
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 (SIWE) message, enhanced by ReCaps (ReCap Capabilities). This enhancement not only offers immediate authentication for dApps, paving the way for prompt user logins, but also integrates informed consent for authorization. Through this mechanism, dApps can request the delegation of specific capabilities to perform actions on behalf of the wallet user. These capabilities, encapsulated within SIWE messages as ReCap URIs, detail the scope of actions authorized by the user in an explicit and human-readable form.
By incorporating ReCaps, this method extends the utility of SIWE messages, allowing dApps to combine authentication with a nuanced authorization model. This model specifies the actions a dApp is authorized to execute on the user's behalf, enhancing security and user autonomy by providing clear consent for each delegated capability. As a result, dApps can utilize these consent-backed messages to perform predetermined actions, significantly enriching the interaction between dApps, wallets, and users within the Ethereum ecosystem.
#### Handling Authentication Requests
To handle incoming authentication requests, subscribe to the `session_authenticate` event. This will notify you of any authentication requests that need to be processed, allowing you to either approve or reject them based on your application logic.
```typescript
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)
});
```
#### Populate 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
**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
});
```
#### Configure Networking and Pair Clients
Confirm you have configured the Network and Pair Client first
* [Networking](/advanced/api/core/relay)
* [Pairing](/advanced/api/core/pairing)
#### Configure Sign Client
In order to initialize a client, call a `configure` method on the Sign instance
```swift
Sign.configure(crypto: CryptoProvider)
```
#### Subscribe for Sign Publishers
The following publishers are available to subscribe:
```swift
public var sessionsPublisher: AnyPublisher<[Session], Never>
public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never>
public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never>
public var socketConnectionStatusPublisher: AnyPublisher
public var sessionSettlePublisher: AnyPublisher
public var sessionDeletePublisher: AnyPublisher<(String, Reason), Never>
public var sessionResponsePublisher: AnyPublisher
public var sessionRejectionPublisher: AnyPublisher<(Session.Proposal, Reason), Never>
public var sessionUpdatePublisher: AnyPublisher<(sessionTopic: String, namespaces: [String : SessionNamespace]), Never>
public var sessionEventPublisher: AnyPublisher<(event: Session.Event, sessionTopic: String, chainId: Blockchain?), Never>
public var sessionUpdateExpiryPublisher: AnyPublisher<(sessionTopic: String, expiry: Date), Never>
```
#### Connect Clients
Your Wallet should allow users to scan a QR code generated by dapps. You are responsible for implementing it on your own.
For testing, you can use our test dapp at: [https://react-app.walletconnect.com/](https://react-app.walletconnect.com/), which is v2 protocol compliant.
Once you derive a URI from the QR code call `pair` method:
```swift
try await Pair.instance.pair(uri: uri)
```
if everything goes well, you should handle following event:
```swift
Sign.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 `ProposalNamespaces` that contains required 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 [cloud](https://cloud.reown.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"]
}
}
```
#### 💡 AutoNamespaces Builder Utility
`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 {
let 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")!
]
)
try await Sign.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces)
} catch {
print(error)
}
```
#### Approve Session
```swift
Sign.instance.approve(
proposalId: "proposal_id",
namespaces: sessionNamespaces
)
```
#### Reject Session
```swift
Sign.instance.reject(
proposalId: "proposal_id",
reason: .userRejected
)
```
When session is successfully approved `sessionSettlePublisher` will publish a `Session`
```swift
Sign.instance.sessionSettlePublisher
.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
Sign.instance.getSessions()
```
#### Track Sessions
When your `Sign` 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
Sign.instance.sessionsPublisher
.receive(on: DispatchQueue.main)
.sink { [self self] (sessions: [Session]) in
// Reload UI
}.store(in: &publishers)
```
#### Handle Requests from Dapp
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
Sign.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 Sign.instance.respond(topic: request.topic, requestId: request.id, response: .response(response))
```
#### Update Session
If you want to update user session's chains, accounts, methods or events you can use session update method.
```swift
try await Sign.instance.update(topic: session.topic, namespaces: newNamespaces)
```
#### Extend 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 Sign.instance.extend(topic: session.topic)
```
above method will extend a user's session to a week.
#### Disconnect Session
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 Sign.instance.disconnect(topic: session.topic)
```
### Authenticated Session
An authenticated session represents a secure connection established between a wallet and a dApp after successful authentication.
#### 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
Sign.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.
```
#### Building Authentication Objects
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 Sign.instance.buildAuthPayload(
payload: request.payload,
supportedEVMChains: Array(commonChains),
supportedMethods: supportedMethods
)
let formattedMessage = try Sign.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 Sign.instance.buildSignedAuthObject(
authPayload: supportedAuthPayload,
signature: signature,
account: accountForChain
)
authObjects.append(authObject)
}
return authObjects
}
```
#### Approving Authentication Requests
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 Sign.instance.approveSessionAuthenticate(requestId: requestId, auths: authObjects)
```
**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.
#### 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 Sign.instance.rejectSession(requestId: requestId)
```
#### Where to go from here
* Try our example wallet implementation [here](https://github.com/reown-com/reown-swift/tree/main/Example/WalletApp).
{/* */}
* Build API documentation in XCode: go to Product -> Build Documentation
#### **Initialization**
```kotlin
val projectId = "" // Get Project ID at https://cloud.reown.com/
val connectionType = ConnectionType.AUTOMATIC or ConnectionType.MANUAL
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)
val init = Sign.Params.Init(core = CoreClient)
SignClient.initialize(init) { error ->
// Error will be thrown if there's an issue during initialization
}
```
The wallet client will always be responsible for exposing accounts (CAPI10 compatible) to a Dapp and therefore is also in charge of signing.
To initialize the Sign client, create a `Sign.Params.Init` object in the Android Application class with the Core Client. The `Sign.Params.Init` object will then be passed to the `SignClient`initialize function.
# **Wallet**
#### **SignClient.WalletDelegate**
```kotlin
val walletDelegate = object : SignClient.WalletDelegate {
override fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal, verifyContext: Sign.Model.VerifyContext) {
// Triggered when wallet receives the session proposal sent by a Dapp
}
val onSessionAuthenticate: ((Sign.Model.SessionAuthenticate, Sign.Model.VerifyContext) -> Unit)? get() = null
// Triggered when wallet receives the session authenticate sent by a Dapp
override fun onSessionRequest(sessionRequest: Sign.Model.SessionRequest, verifyContext: Sign.Model.VerifyContext) {
// Triggered when a Dapp sends SessionRequest to sign a transaction or a message
}
override fun onSessionDelete(deletedSession: Sign.Model.DeletedSession) {
// Triggered when the session is deleted by the peer
}
override fun onSessionSettleResponse(settleSessionResponse: Sign.Model.SettledSessionResponse) {
// Triggered when wallet receives the session settlement response from Dapp
}
override fun onSessionUpdateResponse(sessionUpdateResponse: Sign.Model.SessionUpdateResponse) {
// Triggered when wallet receives the session update response from Dapp
}
override fun onConnectionStateChange(state: Sign.Model.ConnectionState) {
//Triggered whenever the connection state is changed
}
override fun onError(error: Sign.Model.Error) {
// Triggered whenever there is an issue inside the SDK
}
}
SignClient.setWalletDelegate(walletDelegate)
```
`Sign.Model.VerifyContext` provides a domain verification information about SessionProposal and SessionRequest. 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 SignClient needs a `SignClient.WalletDelegate` passed to it for it to be able to expose asynchronous updates sent from the Dapp.
#
#### **Session Approval**
NOTE: addresses provided in `accounts` array should follow [CAPI10](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, Sign.Model.Namespaces.Session(accounts, methods, events))
val approveParams: Sign.Params.Approve = Sign.Params.Approve(proposerPublicKey, namespaces)
SignClient.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 `SignClient.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: Sign.Params.Reject = Reject(proposerPublicKey, rejectionReason, rejectionCode)
SignClient.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 `SignClient.rejectSession` function.
#
#### **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 = Sign.Params.Disconnect(sessionTopic, disconnectionReason, disconnectionCode)
SignClient.disconnect(disconnectParams) { error -> /*callback for error while disconnecting a session*/ }
```
To disconnect from a settled session, pass a disconnection reason with code and the Session topic to the `SignClient.disconnect`
function.
#
#### **Respond Request**
```kotlin
val sessionTopic: String = /*Topic of Session*/
val jsonRpcResponse: Sign.Model.JsonRpcResponse.JsonRpcResult = /*Settled Session Request ID along with request data*/
val result = Sign.Params.Response(sessionTopic = sessionTopic, jsonRpcResponse = jsonRpcResponse)
SignClient.respond(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 `Sign.Params.Response` with the session's topic and request
ID along with the respond data to the `SignClient.respond` function.
#### **Reject Request**
```kotlin
val sessionTopic: String = /*Topic of Session*/
val jsonRpcResponseError: Sign.Model.JsonRpcResponse.JsonRpcError = /*Session Request ID along with error code and message*/
val result = Sign.Params.Response(sessionTopic = sessionTopic, jsonRpcResponse = jsonRpcResponseError)
SignClient.respond(result) { error -> /*callback for error while responding session request*/ }
```
To reject a JSON-RPC method that was sent from a Dapps for a session, submit a `Sign.Params.Response` with the settled session's topic and
request ID along with the rejection data to the `SignClient.respond` function.
#
#### **Session Update**
NOTE: addresses provided in `accounts` array should follow [CAIP10](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md)
semantics and syntax.
```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 authorized chains*/
val methods: List = /*List of methods that wallet approves*/
val events: List = /*List of events that wallet approves*/
val namespaces: Map = mapOf(namespace, Sign.Model.Namespaces.Session(accounts, methods, events))
val updateParams = Sign.Params.Update(sessionTopic, namespaces)
SignClient.update(updateParams) { error -> /*callback for error while sending session update*/ }
```
To update a session with namespaces, use `SignClient.Update` to submit a `Sign.Params.Update` object with the session's topic and updated namespace objects (i.e. adding requesting new methods or events, new accounts on authorized chains, or authorizing new chainIds within a multi-chain namespace).
#
#### **Session Extend**
```kotlin
val sessionTopic: String = /*Topic of Session*/
val extendParams = Sign.Params.Extend(sessionTopic = sessionTopic)
SignClient.extend(extendParams) { error -> /*callback for error while extending a session*/ }
```
To extend a session, create a `Sign.Params.Extend` object with the session's topic to update the session with to `Sign.Extend`. Session is
extended by 7 days.
#### **Session Ping**
```kotlin
val sessionTopic: String = /*Topic of Session*/
val pingParams = Sign.Params.Ping(sessionTopic)
val listener = object : Sign.Listeners.SessionPing {
override fun onSuccess(pingSuccess: Model.Ping.Success) {
// Topic being pinged
}
override fun onError(pingError: Model.Ping.Error) {
// Error
}
}
SignClient.ping(pingParams, listener)
```
To ping a peer with a session, call `SignClient.ping` with the `Sign.Params.Ping` with a session's topic. If ping is successful, topic is
echo'd in listener.
#
#### **Authenticated Session**
An authenticated session represents a secure connection established between a wallet and a dApp after successful authentication.
### Authentication Requests
To handle incoming authentication requests, set up SignClient.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
}
```
### Responding Authentication Request
To interact with authentication requests, build authentication objects (Sign.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 =
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 = formatAuthMessage(Sign.Params.FormatMessage(authPayloadParams, issuer))
val signature = signMessage(message: formattedMessage, privateKey: privateKey) //Note: Assume `signMessage` is a function you've implemented to sign messages.
val auth = generateAuthObject(authPayloadParams, issuer, signature)
auths.add(auth)
}
}
```
### Approving Authentication Requests
To approve an authentication request, construct Sign.Model.Cacao instances for each supported chain, sign the authentication messages, build AuthObjects and call approveAuthenticate with the request ID and the authentication objects.
```kotlin
val approveAuthenticate = Sign.Params.ApproveAuthenticate(id = sessionAuthenticate.id, auths = auths)
SignClient.approveAuthenticate(approveProposal,
onSuccess = {
//Redirect back to the dapp if redirect is set: sessionAuthenticate.participant.metadata?.redirect
},
onError = { error ->
//Handle error
}
)
```
**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.
### Rejecting Authentication Requests
If the authentication request cannot be approved or if the user chooses to reject it, use the rejectAuthenticate method.
```kotlin
val rejectParams = Sign.Params.RejectAuthenticate(
id = sessionAuthenticate.id,
reason = "Reason"
)
SignClient.rejectAuthenticate(rejectParams,
onSuccess = {
//Success
},
onError = { error ->
//Handle error
}
)
```
#### Setup
First you must setup `SignClientOptions` which stores both the `ProjectId` and `Metadata`. You may also optionally specify the storage module to use. By default, the `FileSystemStorage` module is used if none is specified.
```csharp
var walletOptions = new SignClientOptions()
{
ProjectId = "39f3dc0a2c604ec9885799f9fc5feb7c",
Metadata = new Metadata()
{
Description = "An example wallet to showcase WalletConnectSharpv2",
Icons = new[] { "https://walletconnect.com/meta/favicon.ico" },
Name = "WalletConnectSharpv2 Wallet Example",
Url = "https://walletconnect.com"
},
// Uncomment to disable persistent storage
// Storage = new InMemoryStorage()
};
```
Once you have the options defined, you can initialize the SDK.
```csharp
var walletClient = await WalletConnectSignClient.Init(walletOptions);
```
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
ProposalStruct proposal = await walletClient.Pair(connectData.Uri);
```
The wallet can then approve or reject the proposal using either of the following:
```csharp
string addressToConnect = ...;
var approveData = await walletClient.Approve(proposal, addressToConnect);
await approveData.Acknowledged();
```
```csharp
string[] addressesToConnect = ...;
var approveData = await walletClient.Approve(proposal, addressesToConnect);
await approveData.Acknowledged();
```
```csharp
await walletClient.Reject(proposal, "User rejected");
```
#### WalletConnect Methods
All sign methods require 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 user.
```csharp
var sessionTopic = sessionData.Topic;
```
##### Update 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();
```
##### Extend Session
Extend a session's expiry time so the session remains open
```csharp
var request = await walletClient.Extend(sessionTopic);
await request.Acknowledged();
```
##### Ping
Send a ping to the session
```csharp
var request = await walletClient.Ping(sessionTopic);
await request.Acknowledged();
```
#### 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 statically typed language, so these types must be given whenever you do a session request (or do any querying for session requests).
Currently, **WalletConnectSharp 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)
{
}
}
```
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;
}
```
#### Disconnecting
To disconnect a session, use the `Disconnect` function. You may optional provide a reason for the disconnect
```csharp
await walletClient.Disconnect(sessionTopic);
// or
await walletClient.Disconnect(sessionTopic, Error.FromErrorType(ErrorType.USER_DISCONNECTED));
```
# FAQs
Source: https://docs.reown.com/advanced/faq
## What chains does WalletConnect support?
WalletConnect operates as a chain-agnostic protocol, adhering to the [CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md) standard. While the WalletConnect protocol supports various chains, you can refer to the [list](../cloud/chains/chain-list) for the known compatible blockchains. However, please note that our SDKs have certain limitations on the chains they support.
If you intend to extend support for non-EVM chains in your wallet or dapp, it is recommended to review the cross-chain primitives supported by the WalletConnect protocol through the Chain Agnostic Standards Alliance's [Namespaces](https://namespaces.chainagnostic.org/) project. Additionally, feel free to reach out to our community team for further guidance. In the event that the desired chain lacks documentation in the Namespaces project, 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).
## Will the relay server `bridge.walletconnect.org` still work in v2?
No, the bridge servers are v1 only.
## How can I reconnect to the same pairing if my browser was restarted?
The `signClient` will restore & reconnect its pairings automatically after the page is reloaded. All pairings are stored on the page's `localStorage`.
For more context, feel free to check our [web examples](https://github.com/WalletConnect/web-examples).
## The default relay endpoint is blocked. How can I get around this?
When initializing `signClient`, you can set `relayUrl` to `wss://relay.walletconnect.org`.
```js
const signClient = await SignClient.init({
projectId: "",
relayUrl: "wss://relay.walletconnect.org",
metadata: {},
});
```
## How can we use a custom relay for our bridge without a WC URI parameter as the host?
You are more than welcome to utilize a custom URI parameter during testing. However, it is currently not recommended for use in a production environment.
## Why is self-hosting not an option at this time? Are there plans to make this possible in the future?
We understand the desire for developers to self-host their own relay. We share this vision, and have embarked on a decentralization roadmap in order to achieve this. By the end of this summer, we will launch a permissioned network and invite a select group of partners to participate in this crucial first phase. Our objective is to make self-hosting relay a reality with the creation of the decentralized WalletConnect Network, and we appreciate your patience as we progress in this enormous mission.
## How do I report a security issue?
Please consult our [security.txt](https://reown.com/.well-known/security.txt)
# Dapp Integration Guide
Source: https://docs.reown.com/advanced/multichain/polkadot/dapp-integration-guide
## Dapp Guide Section Topics
* WalletConnect Code/Component Setup
* Constructing unsigned transactions
* Sending unsigned transactions for signing using WalletConnect
* Adding the signature to the ExtrinsicPayload
* Signing and sending the transaction to the node
***
# WalletConnect Code/Component Setup
1. **One time step**: Generate a unique `projectId` by visiting and creating your project’s profile on WalletConnect’s project dashboard at: [https://cloud.reown.com/](https://cloud.reown.com/).
2. Import `UniversalProvider` and `{ WalletConnectModal }` from `@walletconnect/universal-provider` and `@walletconnect/modal` respectively.
```js
import UniversalProvider from "@walletconnect/universal-provider";
import { WalletConnectModal } from "@walletconnect/modal";
```
3. Instantiate a universal provider using the `projectId` created for your app.
```js
const provider = await UniversalProvider.init({
projectId: "2ea3f3ghubh32b8ie2f2",
relayUrl: "wss://relay.walletconnect.com",
});
```
4. On user action (e.g. user clicks connect for WalletConnect), call the connect method on the providers sign client passing in preferred params.
WalletConnect uses chain ids based on the CAIP standard (CAIP-13 for Polkadot Namespace): [Polkadot WalletConnect CAIP-13](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-13.md)
* polkadot CAIP id = `91b171bb158e2d3848fa23a9f1c25182`
* kusama CAIP id = `b0a8d493285c2df73290dfb7e61f870f`
* westend CAIP id = `e143f23803ac50e8f6f8e62695d1ce9e`
* statemint CAIP id = `68d56f15f85d3136970ec16946040bc1`
* hydradx CAIP id = `afdc188f45c71dacbaa0b62e16a91f72`
* phala network CAIP id = `1bb969d85965e4bb5a651abbedf21a54`
* astar network CAIP id = `9eb76c5184c4ab8679d2d5d819fdf90b`
* crust shadow CAIP id = `d4c0c08ca49dc7c680c3dac71a7c0703`
* mangata kusama mainnet CAIP id = `d611f22d291c5b7b69f1e105cca03352`
* turing network CAIP id = `0f62b701fb12d02237a33b84818c11f6`
* Chain ids correspond to the genesis hash for each respective chain
### Example Namespace and Sign Client connect call:
**Note**: this serves as an example where a dapp requires 3 different chain namespaces (polkadot, hydradx and turing network). The supported methods, chains, and events can all be defined by the dapp team based on the requirements of the dapp.
```js
const params = {
requiredNamespaces: {
polkadot: {
methods: ["polkadot_signTransaction", "polkadot_signMessage"],
chains: [
"polkadot:91b171bb158e2d3848fa23a9f1c25182", // polkadot
"polkadot:afdc188f45c71dacbaa0b62e16a91f72", // hydradx
"polkadot:0f62b701fb12d02237a33b84818c11f6", // turing network
],
events: ['chainChanged", "accountsChanged'],
},
},
};
const { uri, approval } = await provider.client.connect(params);
```
5. Create a standalone modal using your dapps WalletConnect projectId.
```js
const walletConnectModal = new WalletConnectModal({
projectId: "2ea3f3ghubh32b8ie2f2",
});
```
6. Open the modal prompting the user to scan the QR code with their wallet app or copy the URI from the modal and paste into their wallet app to begin the session creation process.
```js
// if there is a URI from the client connect step open the modal
if (uri) {
walletConnectModal.openModal({ uri });
}
// await session approval from the wallet app
const walletConnectSession = await approval();
```
7. Get the accounts from the session for use in constructing transactions.
```js
const walletConnectAccount = Object.values(walletConnectSession.namespaces)
.map((namespace) => namespace.accounts)
.flat();
// grab account addresses from CAIP account formatted accounts
const accounts = wcAccounts.map((wcAccount) => {
const address = wcAccount.split(":")[2];
return address;
});
```
# Constructing Unsigned Transactions
One thing the dapp must do is properly construct the unsigned transaction. This consists of constructing an object with the intended chain’s metadata including the `specVersion`, `transactionVersion`, etc. In addition to these, you must provide the transaction data (`method`, `address` etc). Below is an example of what this general structure looks like using polkadotjs but this can be done in any valid way:
```js
const unsignedTransaction = {
specVersion: api.runtimeVersion.specVersion.toHex(),
transactionVersion: api.runtimeVersion.transactionVersion.toHex(),
address: selectedWalletConnectAccountAddress,
blockHash: lastHeader.hash.toHex(),
blockNumber: blockNumber.toHex(),
era: era.toHex(),
genesisHash: api.genesisHash.toHex(),
method: method.toHex(),
nonce: nonce.toHex(),
signedExtensions: [
"CheckNonZeroSender",
"CheckSpecVersion",
"CheckTxVersion",
"CheckGenesis",
"CheckMortality",
"CheckNonce",
"CheckWeight",
"ChargeTransactionPayment",
],
tip: api.registry.createType("Compact", 0).toHex(),
version: tx.version,
};
```
8. A specific example of constructing an unsigned transaction using polkadotjs api to retrieve the chains metadata.
```js
// import api and wsprovider
import { ApiPromise, WsProvider } from "@polkadot/api";
//instantiate wsProvider and api
const wsProvider = new WsProvider("wss://rpc.polkadot.io");
const api = await ApiPromise.create({ provider: wsProvider });
const lastHeader = await api.rpc.chain.getHeader();
const blockNumber = api.registry.createType(
"BlockNumber",
lastHeader.number.toNumber()
);
const tx = api.tx.balances.transfer(keyring.bob.publicKey, 100);
const method = api.createType("Call", tx);
const era = api.registry.createType("ExtrinsicEra", {
current: lastHeader.number.toNumber(),
period: 64,
});
const accountNonce = getBalanceAccount(submitAddress)?.nonce || 0;
const nonce = api.registry.createType("Compact", accountNonce);
const unsignedTransaction = {
specVersion: api.runtimeVersion.specVersion.toHex(),
transactionVersion: api.runtimeVersion.transactionVersion.toHex(),
address: selectedWalletConnectAccountAddress,
blockHash: lastHeader.hash.toHex(),
blockNumber: blockNumber.toHex(),
era: era.toHex(),
genesisHash: api.genesisHash.toHex(),
method: method.toHex(),
nonce: nonce.toHex(),
signedExtensions: [
"CheckNonZeroSender",
"CheckSpecVersion",
"CheckTxVersion",
"CheckGenesis",
"CheckMortality",
"CheckNonce",
"CheckWeight",
"ChargeTransactionPayment",
],
tip: api.registry.createType("Compact", 0).toHex(),
version: tx.version,
};
```
# Sending unsigned transactions to the wallet for signing using WalletConnect
9. Send the unsigned transaction to the paired wallet for signing using the providers sign client. This triggers a `session_request` event which must be handled by the paired wallet.
### Polkadot Example
```js
const result = await client.request({
chainId: "polkadot:91b171bb158e2d3848fa23a9f1c25182",
topic: walletConnectSession.topic,
request: {
method: "polkadot_signTransaction",
params: {
address: selectedWalletConnectAddress,
transactionPayload: unsignedTransaction,
},
},
});
```
### Parachain Example (HydraDX)
```js
const result = await client.request({
chainId: "polkadot:afdc188f45c71dacbaa0b62e16a91f72",
topic: walletConnectSession.topic,
request: {
method: "polkadot_signTransaction",
params: {
address: selectedWalletConnectAddress,
transactionPayload: unsignedTransaction,
},
},
});
```
Once the request is resolved, the expected response should be a result object which contains a signature
```js
{
signature: "0x09u03f0h3nf34f0m3mn0fn34fn3f"; // an example result
}
```
## Adding the signature to the ExtrinsicPayload
Using this signature, we can now create an `ExtrinsicPayload` and add the signature. Below is an example of this general step:
```js
// create the extrinsic payload using the unsigned transaction
const rawUnsignedTransaction = api.registry.createType(
"ExtrinsicPayload",
unsignedTransaction,
{
version: unsignedTransaction.version,
}
);
// add the signature to the extrinsic payload
tx.addSignature(
selectedWalletConnectAddress,
result.signature,
rawUnsignedTransaction
);
```
# Signing and sending the transaction to the node
Now, it is just about attaching the returned signature to the transaction and submitting it as specified by the dApp. Below is a specific example showing this process:
```js
const rawUnsignedTransaction = api.registry.createType(
"ExtrinsicPayload",
unsignedTransaction,
{
version: unsignedTransaction.version,
}
);
tx.addSignature(
selectedWalletConnectAddress,
result.signature,
rawUnsignedTransaction
);
// send the signed transaction to the node
const unsub = await tx.send(({ status, events }) => {
// optionally handle ready status, notify user of submission
if (status.isReady) {
// ...
}
// optionally handle in block status, notify user of in block
if (status.isInBlock) {
// ...
}
// let user know outcome of transaction
if (status.isFinalized) {
events.forEach(({ event: { method } }) => {
// if success optionally notify/update state
if (method === "ExtrinsicSuccess") {
// ...
unsub(); // unsubscribe from extrinsic
} else if (method === "ExtrinsicFailed") {
// on failure optionally notify/update state
// ...
unsub(); // unsubscribe from extrinsic
}
});
}
});
```
# Namespaces Guide
Source: https://docs.reown.com/advanced/multichain/polkadot/namespaces-guide
## Guide Section Topics
* [Understanding Namespaces](#understanding-namespaces)
* [Proposal Namespaces](#proposal-namespaces)
* [Proposal Namespace Example](#proposal-namespace-example)
* [Session Namespaces](#session-namespaces)
* [Session Namespace Example](#session-namespace-example)
* [Chains](#chains), [Methods](#methods) and [Events](#events)
* [Using Namespaces with the Universal Provider](#dapps-universal-provider-and-namespaces)
* [Using Namespaces with the WalletKit](#wallets-walletkit-and-namespaces)
### Understanding Namespaces
* Pairing sessions use specific methods, events and chains during their lifetimes. These arguments constitute what is known as a `namespace`.
* Namespaces are used to specify the chains, methods and events that are intended to be used in a particular session.
* They establish the minimal requirement for a wallet and a dapp to get paired. There are two types of namespaces, `proposal namespaces` and `session namespaces`.
### Proposal Namespaces
* A dapp sends a proposal namespace to the wallet for pairing. The proposal namespace contains the list of `chains`, `methods` and `events` that the dapp intends to make use of.
* The wallet validates if the received proposal namespaces are valid and returns a session with its approved namespaces as a response if it is valid along with the approved accounts for each chain in the namespace.
* If the requested proposal namespaces are not valid based on the wallets rules, the session cannot be established and the wallet rejects it with an error code that tells the dapp if the proposal namespaces have invalid chains, methods, events or if it was simply rejected by the user.
### Proposal Namespace Example
An example Proposal Namespace for a dapp which supports connecting to Polkadot, Ethereum, Polygon and Cosmos:
```js
{
"polkadot": {
"chains": [
"polkadot:91b171bb158e2d3848fa23a9f1c25182", // Polkadot
"polkadot:b0a8d493285c2df73290dfb7e61f870f", // Kusama
],
"methods": ["polkadot_signMessage"],
"events": ["accountsChanged"]
},
"eip155": {
"chains": [
"eip155:1", // Ethereum
"eip155:137" // Polygon
],
"methods": ["eth_sign"],
"events": ["accountsChanged"]
},
"cosmos": {
"chains": ["cosmos:cosmoshub-4"], // Cosmos
"methods": ["cosmos_signDirect"],
"events": ["someCosmosEvent"]
}
}
```
### Session Namespaces
* The wallet validates if the received proposal namespaces match with the session namespaces it supports. If they match, a session is established successfully and pairing is completed. If not, the session is not established.
* The wallet session can also choose to provide access to more chains, methods or events that were not a part of the proposal namespaces. This means a dapp could send a proposal namespace with only Polkadot `['polkadot:91b171bb158e2d3848fa23a9f1c25182']` in its requiredNamespaces `chains` field but a wallet could return a session namespace with both Polkadot and Kusama `['polkadot:91b171bb158e2d3848fa23a9f1c25182','polkadot:b0a8d493285c2df73290dfb7e61f870f']` as part of the sessions namespaces.
### Session Namespace Example
```js
{
"polkadot": {
"accounts": [
"polkadot:91b171bb158e2d3848fa23a9f1c25182:AZBEwbZhYeiofodZnM2iAoshP3pXRPNSJEKFqEPDmvv1mY7"
]
"methods": ["polkadot_signMessage", "polkadot_signTransaction"],
"events": ["accountsChanged"]
},
"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"]
}
}
```
### Chains
* `chains` is an array of chain ids which represent the chains the session will be using during its lifetime. For Polkadot, the format for each chain id is the chain agnostic namespace (e.g. `eip155`, `polkadot`, `cosmos` etc) followed by a colon and the genesis hash for the chain (e.g. `91b171bb158e2d3848fa23a9f1c25182` for Polkadot).
* A dapp or wallet can make as many or as few chain ids a part of its namespace as desired.
### Methods
* `methods` is represented as an array of wallet defined methods that a session supports.
* These are not pre-defined or centrally implemented and can be modified/extended as needed by a wallet.
* In the above Polkadot session namespace example there are two given methods `polkadot_signMessage` and `polkadot_signTransaction`. The idea for the functionality of these methods is to sign the relevant data (either a message or unsigned transaction) and return the signature. [An example for each method](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/lib/PolkadotLib.ts).
* If a dapp required additional method support such as receiving the signed hex for a transaction in order to submit it rather than the signature, a wallet only needs to define and add support for the method so that any dapp that requires that functionality can use it when making requests.
* An example would be adding a method named `polkadot_getSignedHex` and creating an implementation that signs, and returns the hash of the signed transaction.
```js
// Example Session Namespace
{
"polkadot": {
"accounts": [
"polkadot:91b171bb158e2d3848fa23a9f1c25182:AZBEwbZhYeiofodZnM2iAoshP3pXRPNSJEKFqEPDmvv1mY7"
],
"methods": [
"polkadot_signMessage",
"polkadot_signTransaction"
"polkadot_getSignedHex",
],
"events": ["accountsChanged"],
}
}
// In the wallets codebase, you'd add functionality to be called when this new method is called from a WalletConnect session
// Specific Example Implementation:
public async getSignedHex(payload: SignerPayloadJSON) {
this.registry.setSignedExtensions(payload.signedExtensions)
const txPayload = this.registry.createType('ExtrinsicPayload', payload, {
version: payload.version
});
const { signature } = txPayload.sign(this.keypair)
const extrinsic = registry.createType(
'Extrinsic',
{ method: payload.method },
{ version: payload.version }
);
extrinsic.addSignature(unsigned.address, signature, unsigned);
const hex = extrinsic.toHex();
return { hex };
}
```
* Wallets and dapps can define an agreed upon interface based on a particular chain or ecosystems needs.
### Events
* `events` represent specific changes in a sessions state that a dapp or wallet may want to take some action on.
* For example, a dapp or a wallet might want to perform some action if the user changes the selected session accounts. An example of emitting this event can be found below:
```js
await signClient.emit({
topic,
event: {
name: "accountsChanged",
data: ["AZBEwbZhYeiofodZnM2iAoshP3pXRPNSJEKFqEPDmvv1mY7"],
},
chainId: "polkadot:91b171bb158e2d3848fa23a9f1c25182",
});
```
This can be useful in a wallet if a user is adding additional accounts to a session so that the wallet or dapp can respond and update their respective states using events such as `session_update` to update the accounts, chains, methods or events for the session or `session_delete` to end a session. [(More on events)](https://docs.walletconnect.com/specs/clients/sign/session-events#session_request).
### Using Namespaces
In order to create a session proposal, call the connect method on the universal provider's sign client. The sign clients `connect` method accepts an object based on the following interface:
```js
interface ConnectParams {
requiredNamespaces?: ProposalTypes.RequiredNamespaces;
optionalNamespaces?: ProposalTypes.OptionalNamespaces;
sessionProperties?: ProposalTypes.SessionProperties;
pairingTopic?: string;
relays?: RelayerTypes.ProtocolOptions[];
}
```
### Dapps: Universal Provider and Namespaces:
The connect method on the universal provider expects an object that matches the above `ConnectParams` interface. All fields are optional and in the below example we use only the `requiredNamespaces` field in our proposal namespace:
```js
const proposalNamespace = {
requiredNamespaces: {
polkadot: {
methods: ["polkadot_signTransaction", "polkadot_signMessage"],
chains: ["polkadot:91b171bb158e2d3848fa23a9f1c25182"],
events: ["chainChanged", "accountsChanged"],
},
},
};
// call connect on the universal provider passing the proposal namespace
const { uri, approval } = await provider.client.connect(proposalNamespace);
```
### Wallets: WalletKit and Namespaces:
When the WalletKit approves and creates a session, it must provide the session proposal `id` as well as the session `namespaces` which are approved for use in the session. An example of what this looks like is below.
```js
const session = await walletKit.approveSession({
id: proposal.id,
namespaces: {
polkadot: {
accounts: [
"polkadot:91b171bb158e2d3848fa23a9f1c25182:AZBEwbZhYeiofodZnM2iAoshP3pXRPNSJEKFqEPDmvv1mY7",
],
methods: ["polkadot_signTransaction", "polkadot_signMessage"],
chains: ["polkadot:91b171bb158e2d3848fa23a9f1c25182"],
events: ["chainChanged", "accountsChanged"],
},
},
});
```
More information on namespaces can be found [here](https://docs.walletconnect.com/specs/clients/sign/namespaces#controller-side-validation-of-incoming-proposal-namespaces-wallet).
# Wallet Integration Guide
Source: https://docs.reown.com/advanced/multichain/polkadot/wallet-integration-guide
## Wallet Guide Section Topics
* WalletConnect Code/Component Setup
* Approving a Session Proposal
* Rejecting a Session Proposal
* Handling Session Request Events
* Session Persistence/Management
***
1. **Getting Started:** Generate a unique `projectId` by visiting and creating your project's profile on WalletConnect's project dashboard at:
* `https://cloud.reown.com/`
# WalletConnect Code/Component Setup
2. Import Core and WalletKit from WalletConnect.
```js
import { Core } from "@walletconnect/core";
import { WalletKit } from "@walletconnect/wallekit";
```
3. Instantiate and add Core and WalletKit to the state of the wallet.
```js
const core = new Core({ projectId: "fgu234234njbhvhv23525bj" });
const walletKit = await WalletKit.init({
core: core,
metadata: {
name: "Example WalletConnect Wallet",
description: "Example WalletConnect Integration",
url: "myexamplewallet.com",
icons: [],
},
});
```
4. Create a function to accept a session `uri` which will be passed from a dapp when a user either scans the dapp's WalletConnect qrcode modal or manually copies and pastes the uri from the modal into the wallet's UI.
```js
const onConnect = async (uri: string) => {
// call walletKit.core.pairing.pair( { uri: uri })
// with the uri received from the dapp in order to emit the
// `session_proposal` event
const result = await walletKit.core.pairing.pair({ uri });
};
```
5. Handle the `session_proposal` event on the `walletKit`. This event is triggered when the `pair` method is called on `walletKit.core.pairing` to create a pairing session.
# Approving a Session Proposal (Example)
When approving a session proposal, the wallet can perform any necessary checks such as ensuring that the proposal includes all required namespaces and any optional namespaces. The approval response also contains the approved accounts as part of the namespace. Below is an example showing the format for wallet accounts and how to include them in a session proposal approval.
```js
// example account addresses in wallet state
const substrateAccounts = [
"5CK8D1sKNwF473wbuBP6NuhQfPaWUetNsWUNAAzVwTfxqjfr",
"5F3sa2TJAWMqDhXG6jhV4N8ko9SxwGy8TpaNS1repo5EYjQX",
];
// format the accounts to match the chain:chain_id:address format
const walletConnectAccounts = accounts.map(
(account) => `polkadot:91b171bb158e2d3848fa23a9f1c25182:${account.address}`
);
walletKit.on("session_proposal", async (proposal) => {
// optionally show user a modal or way to reject or approve session
showWalletConnectModal();
// handle user approval case
// create the approved session with selected accounts, supported methods, chains and events for your wallet
const session = await walletKit.approveSession({
id: proposal.id,
namespaces: {
polkadot: {
accounts: walletConnectAccounts,
methods: ["polkadot_signTransaction", "polkadot_signMessage"],
chains: ["polkadot:91b171bb158e2d3848fa23a9f1c25182"],
events: ['chainChanged", "accountsChanged'],
},
},
});
// create response object
const response = {
id: proposal.id,
result: "session approved",
jsonrpc: "2.0",
};
// respond to the dapp request with the approved session's topic and response
await walletKit.respondSessionRequest({ topic: session.topic, response });
});
```
# Rejecting a Session Proposal (Example)
If the user does not approve the requested chains, methods, or accounts, or if the wallet does not support the requested chains or methods, the response should not be considered a success. Below is an example of rejecting a session proposal.
```js
// Note: session_request is emitted when the client on the dapp end calls the request method
// import getSdkError to create predefined ErrorResponse types
import { getSdkError } from "@walletconnect/utils";
walletKit.on("session_proposal", async (proposal) => {
// optionally show user a modal or way to reject or approve session
showWalletConnectModal();
// handle user reject action
await walletKit.rejectSession({
id: proposal.id,
reason: getSdkError("USER_REJECTED"),
});
});
```
# Handling Session Request Event
A dapp triggers an event when it requires the wallet to carry out a specific action, such as signing a transaction. The event includes a topic and a request object, which will differ based on the requested action. As seen in the [WalletConnect Web Examples](https://github.com/WalletConnect/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/lib/PolkadotLib.ts), two common use cases in polkadot are signing messages and signing transactions. These methods are represented here as `polkadot_signMessage` and `polkadot_signTransaction` respectively and each simply signs the respective payload and returns the signature to the dapp. An example of a `session_request` event handler containing both can be found below.
```js
walletKit.on("session_request", async (requestEvent) => {
const { params, id } = requestEvent;
const { request } = params;
const address = request.params?.address;
// check that the request address is in your users list of wallets
// Example:
const wallet = getPolkadotWallet(address); //
if (!wallet) {
throw new Error("Polkadot wallet does not exist");
}
// handle supported methods (polkadot_signMessage, polkadot_signTransaction)
switch (request.method) {
case "polkadot_signMessage":
// call function used by wallet to sign message and return the signature
const signature = await yourwallet.signMessage(request.params.message);
// create the response containing the signature in the result
const response = { id, result: { signature: signature }, jsonrpc: "2.0" };
// respond to the dapp request with the response and topic
await walletKit.respondSessionRequest({ topic, response });
case "polkadot_signTransaction":
// call function used by wallet to sign transactions and return the signature
const signature = await yourwallet.signTransaction(
request.params.transactionPayload
);
// create the response containing the signature in the result
const response = { id, result: { signature: signature }, jsonrpc: "2.0" };
// respond to the dapp request with the response and topic
await walletKit.respondSessionRequest({ topic, response });
// throw error for methods your wallet doesn't support
default:
throw new Error(getSdkError("INVALID_METHOD").message);
}
});
```
# Sessions Persistence/Management
* sessions can be saved/stored so users don't have to pair repeatedly
* sessions can be disconnected from using `await walletKit.disconnectSession({ topic: topic });` passing the session topic.
* sessions can be extended using `await walletKit.extendSession({ topic: topic });` passing the session topic.
* Default session lifetime is 7 days for WalletConnect v2.0.
# Further Documentation for WalletConnect 2.0
* [https://docs.walletconnect.com/](https://docs.walletconnect.com/)
# Bitcoin
Source: https://docs.reown.com/advanced/multichain/rpc-reference/bitcoin-rpc
Bitcoin JSON-RPC Methods
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
// 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".
### 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
// 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
// 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"
}
}
```
# Casper
Source: https://docs.reown.com/advanced/multichain/rpc-reference/casper-rpc
## Methods
### casper\_sign\_deploy
Use the `casper_sign_deploy` method to request the user to approve or reject the signature of a transaction (aka deploy). The wallet should validate the received deploy object and present the details to the user for his review.
#### Parameters
* `address`. Type: `string`. The chain namespace and the public key corresponding to the key pair that signs the transaction are separated with a colon character.
* `deploy`. Type: `object`. A Deploy object as per the Casper protocol specification.
#### Returns
* `deploy`. Type: `object`. The Deploy object including the newly generated approval item as per the Casper protocol specification.
If the user rejects the signature, the wallet returns the WalletConnect SDK error `USER_REJECTED`.
#### Example
Request:
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "casper_sign_deploy",
"params": {
"deploy": {
"hash": "a3301c9da7f0183f1c8904bed7fc72cf563454509462cada378b3f42a92f7b4f",
"header": {
"account": "02032E126170e5f28443775330B5B5Fe29dCE1a1dD3269910349525935ccbaf352EA",
"timestamp": "2024-02-01T08:41:59.207Z",
"ttl": "30m",
"gas_price": 1,
"body_hash": "188d88eedd3dc64b4ac3f8ca9b74be2b2fa588e2d537875d22b7e1a68658d19e",
"dependencies": [],
"chain_name": "casper-test"
},
"payment": {
"ModuleBytes": {
"module_bytes": "",
"args": [["amount", { "bytes": "0400e1f505", "cl_type": "U512" }]]
}
},
"session": {
"Transfer": {
"args": [
["amount", { "bytes": "0500f2052a01", "cl_type": "U512" }],
[
"target",
{
"bytes": "0202e99759649fa63a72c685b72e696b30c90f1deabb02d0d9b1de45eb371a73e5bb",
"cl_type": "PublicKey"
}
],
[
"id",
{ "bytes": "01d204000000000000", "cl_type": { "Option": "U64" } }
]
]
}
},
"approvals": []
},
"address": "casper:casper-test:02032E126170e5f28443775330B5B5Fe29dCE1a1dD3269910349525935ccbaf352EA"
}
}
```
Response:
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"deploy": {
"hash": "a3301c9da7f0183f1c8904bed7fc72cf563454509462cada378b3f42a92f7b4f",
"header": {
"account": "02032E126170e5f28443775330B5B5Fe29dCE1a1dD3269910349525935ccbaf352EA",
"timestamp": "2024-02-01T08:41:59.207Z",
"ttl": "30m",
"gas_price": 1,
"body_hash": "188d88eedd3dc64b4ac3f8ca9b74be2b2fa588e2d537875d22b7e1a68658d19e",
"dependencies": [],
"chain_name": "casper-test"
},
"payment": {
"ModuleBytes": {
"module_bytes": "",
"args": [["amount", { "bytes": "0400e1f505", "cl_type": "U512" }]]
}
},
"session": {
"Transfer": {
"args": [
["amount", { "bytes": "0500f2052a01", "cl_type": "U512" }],
[
"target",
{
"bytes": "0202e99759649fa63a72c685b72e696b30c90f1deabb02d0d9b1de45eb371a73e5bb",
"cl_type": "PublicKey"
}
],
[
"id",
{ "bytes": "01d204000000000000", "cl_type": { "Option": "U64" } }
]
]
}
},
"approvals": [
{
"signer": "02032E126170e5f28443775330B5B5Fe29dCE1a1dD3269910349525935ccbaf352EA",
"signature": "02ad07c25d7cef27598f67c7bafce3e07e4198de7884f0e48041965c0f0be2690956d25bae0510bec9463da4aa6a5e591fb3cb88c8f31df85bc0b6f857b80f64e2"
}
]
}
}
}
```
### casper\_sign\_message
Use `casper_sign_message` to request the user to sign a message. It's recommended to use this method with human-readable text messages. Upon user approval, the wallet must generate a signature for the prefixed message `"Casper Message:\n" + message`. The prefix protects the user against misuse of this method, preventing a malicious actor from trying to trick the user into signing arbitrary data, like a network transaction.
#### Parameters
* `address`. Type: `string`. The chain namespace and the public key corresponding to the key pair that signs the transaction separated with a colon character.
* `message`. Type: `string`. The message to be signed.
#### Returns
* `signature`. Type: `string`. The signature of the message.
If the user rejects the signature, the wallet returns the WalletConnect SDK error `USER_REJECTED`.
#### Example
Request:
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "casper_sign_message",
"params": {
"message": "CSPR.studio wants you to sign in with your Casper account:\n0x01953...808f3 \n\nIssued At: 07/21/2023 10:07:25\nnonce: 428b62e4",
"address": "casper:casper-test:0202a8e3e5E32800792F37F738d95BF2610d86E97922D13ab97945bb062824ed9E8A"
}
}
```
Response:
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "casper_sign_message",
"result": {
"signature": "b52482afd2392b715cc43d9ad9f1f7067752a10ba5b49b89bc61b398e478841e6d8a4a224aeb944a34f23d98a232cdab6e5a60a5e886e8b0719d7b84277c405f"
}
}
```
## Events
Currently, this specification doesn't define any required events for wallets.
# Cosmos
Source: https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc
Cosmos JSON-RPC Methods
## cosmos\_getAccounts
This method returns an array of key pairs available to sign from the wallet mapped with an associated algorithm and address on the blockchain.
### Parameters
none
### Returns
1.`Array` - Array of accounts:
1.1. `Object` - Account Data object with parameters:
1.1.1. `algo` : `STRING` - algorithm used for signing
1.1.2. `address` : `STRING` - corresponding address for keypair
1.1.3. `pubkey` : `STRING` - base64 encoded public key for keypair
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "cosmos_getAccounts",
"params": {}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"algo": "secp256k1",
"address": "cosmos1sguafvgmel6f880ryvq8efh9522p8zvmrzlcrq",
"pubkey": "AgSEjOuOr991QlHCORRmdE5ahVKeyBrmtgoYepCpQGOW"
}
]
}
```
## cosmos\_signDirect
This method returns a signature for the provided document to be signed targeting the requested signer address corresponding to the keypair returned by the account data.
### Parameters
1. `Object` - Signing parameters:
1.1. `signerAddress` : `STRING` - corresponding address for keypair
1.2. `signDoc` : `Object` - Document to be signed:
1.2.2. `chainId` : `STRING` - identifier of blockchain
1.2.1. `accountNumber` : `STRING` - blockchain account number
1.2.3. `authInfoBytes` : `DATA` - encoded authentication information
1.2.4. `bodyBytes` : `DATA` - encoded body of message to sign
### Returns
1. `Object` - Signing parameters:
1.1. `signature` : `Object` - corresponding signature for signed documented
1.1.1. `pub_key` : `Object` - public key for keypair
1.1.1.1: `type` : `STRING` - type of public key
1.1.1.2: `value` : `STRING` - value of public key
1.1.2. `signature`: `STRING` - corresponding signature for signed documented
1.2. `signed` : `Object` - Signed document:
1.2.2. `chainId` : `STRING` - identifier of blockchain
1.2.1. `accountNumber` : `STRING` - blockchain account number
1.2.3. `authInfoBytes` : `DATA` - encoded authentication information
1.2.4. `bodyBytes` : `DATA` - encoded body of message to sign
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "cosmos_signDirect",
"params": {
"signerAddress": "cosmos1sguafvgmel6f880ryvq8efh9522p8zvmrzlcrq",
"signDoc": {
"chainId": "cosmoshub-4",
"accountNumber": "1"
"authInfoBytes": "CgoKABIECgIIARgBEhMKDQoFdWNvc20SBDIwMDAQwJoM",
"bodyBytes": "CpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3"
}
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"signature": {
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AgSEjOuOr991QlHCORRmdE5ahVKeyBrmtgoYepCpQGOW"
},
"signature": "AnTrXtS2lr9CBwhTpRa8ZlKcVR9PeIXGaTpvodyJU05QvRKVjIkQfOZl5JhdkfxCY+a6rhwCOYVcbKQTJlMw4w=="
},
"signed": {
"chainId": "cosmoshub-4",
"accountNumber": "1"
"authInfoBytes": "CgoKABIECgIIARgBEhMKDQoFdWNvc20SBDIwMDAQwJoM",
"bodyBytes": "CpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3"
}
}
}
```
## cosmos\_signAmino
This method returns a signature for the provided document to be signed targeting the requested signer address corresponding to the keypair returned by the account data.
### Parameters
1. `Object` - Signing parameters:
1.1. `signerAddress` : `STRING` - corresponding address for keypair
1.2. `signDoc` : `Object` - Document to be signed:
1.2.2. `chain_id` : `STRING` - identifier of blockchain
1.2.1. `account_number` : `STRING` - blockchain account number
1.2.3. `sequence` : `STRING` - blockchain account sequence
1.2.4. `memo` : `STRING` - amino message memo
1.2.5. `msgs` : `Array` - array of amino messages to be signed:
1.2.5.1. `Object` - amino message object:
1.2.5.1.1. - `type` : `STRING` - amino message type
1.2.5.1.2. - `value` : `STRING` - amino message value
1.2.6. `fee` : `Object` - fee description object
1.2.6.1. `amount` : `Array` - array of currency fees:
1.2.6.1.1. `Object` - currency fee description object:
1.2.6.1.1.1. `denom` : `STRING` - currency denomination
1.2.6.1.1.2. `amount` : `STRING` - currency amount
1.2.6.2. `gas` : `STRING` - gas limit for execution
### Returns
1. `Object` - Signing parameters:
1.1. `signature` : `Object` - corresponding signature for signed documented
1.1.1. `pub_key` : `Object` - public key for keypair
1.1.1.1: `type` : `STRING` - type of public key
1.1.1.2: `value` : `STRING` - value of public key
1.1.2. `signature`: `STRING` - corresponding signature for signed documented
1.2. `signed` : `Object` - Signed document:
1.2.2. `chain_id` : `STRING` - identifier of blockchain
1.2.1. `account_number` : `STRING` - blockchain account number
1.2.3. `sequence` : `STRING` - blockchain account sequence
1.2.4. `memo` : `STRING` - amino message memo
1.2.5. `msgs` : `Array` - array of amino messages to be signed:
1.2.5.1. `Object` - amino message object:
1.2.5.1.1. - `type` : `STRING` - amino message type
1.2.5.1.2. - `value` : `STRING` - amino message value
1.2.6. `fee` : `Object` - fee description object
1.2.6.1. `amount` : `Array` - array of currency fees:
1.2.6.1.1. `Object` - currency fee description object:
1.2.6.1.1.1. `denom` : `STRING` - currency denomination
1.2.6.1.1.2. `amount` : `STRING` - currency amount
1.2.6.2. `gas` : `STRING` - gas limit for execution
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "cosmos_signAmino",
"params": {
"signerAddress": "cosmos1sguafvgmel6f880ryvq8efh9522p8zvmrzlcrq",
"signDoc": {
"chain_id": "foochain",
"account_number": "7",
"sequence": "54"
"memo": "hello, world",
"msgs": [],
"fee": { "amount": [], "gas": "23" }
}
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"signature": {
"pub_key": {
"type": "tendermint/PubKeySecp256k1",
"value": "AgSEjOuOr991QlHCORRmdE5ahVKeyBrmtgoYepCpQGOW"
},
"signature": "AnTrXtS2lr9CBwhTpRa8ZlKcVR9PeIXGaTpvodyJU05QvRKVjIkQfOZl5JhdkfxCY+a6rhwCOYVcbKQTJlMw4w=="
},
"signed": {
"chain_id": "foochain",
"account_number": "7",
"sequence": "54"
"memo": "hello, world",
"msgs": [],
"fee": { "amount": [{"denom": "ufoo", "amount": "10000"}], "gas": "23" }
}
}
}
```
# Dogecoin
Source: https://docs.reown.com/advanced/multichain/rpc-reference/dogecoin-rpc
Dogecoin JSON-RPC Methods
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) standard is:
```
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. We use the first external P2PKH (purpose = 44) address as the default account identifier.
For a specific seed phrase and path `m/44'/3'/0'/0/0` we get account 0 with identifier `DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ`. Its total balance is the sum of all UTXO balances on all addresses with derivation paths:
* `m/44'/3'/0'/change/address_index`
If the wallet user changes to account 1 we get path `m/44'/3'/1'/0/0` with identifier `DBcZSePDaMMduBMLymWHXhkE5ArFEvkagU`. Its total balance is the sum of all UTXO balances on all addresses with derivation paths:
* `m/44'/3'/1'/change/address_index`
## sendTransfer
This method is used to sign and submit a transfer of any `amount` of Dogecoin to a single `recipientAddress`, optionally including a `changeAddress` for the change amount and `memo` set as the OP\_RETURN value 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 Dogecoin to send, denominated in satoshis (Dogecoin 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` - The transaction id as a hex string without 0x prefix.
### Example
The example below specifies a simple transfer of 1.23 DOGE (123000000 Satoshi).
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "sendTransfer",
"params": {
"account": "DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ",
"recipient": "DBcZSePDaMMduBMLymWHXhkE5ArFEvkagU",
"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/44'/3'/0'/0/0".
* `intention` : `String` - *(Optional)* Intention of the address, e.g. "payment" or "ordinal".
### 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 address with path `m/44'/3'/0'/0/2` is 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
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "getAccountAddresses",
"params": {
"account": "DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ"
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"address": "DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ",
"path": "m/44'/3'/0'/0/0"
},
{
"address": "DA6rZ9aV3mkz9uxNvddzzbXEEcSPN8SCUS",
"path": "m/44'/3'/0'/0/2"
},
{
"address": "DDtQfA541GQU2KDrY3ofF5F5hsKxkFiUuG",
"path": "m/44'/3'/0'/0/3"
},
{
"address": "D5A6wPFhCNChUiQHGXftD8DiNgc2G7yT1L",
"path": "m/44'/3'/0'/0/4"
},
{
"address": "DFG9R8ENG4mK5gUiU1VRr3FBT13LfWJ4Fb",
"path": "m/44'/3'/0'/1/3"
},
{
"address": "D7rakaGgZvaBH1vGTxnsQ3ZdV7ejX57hRy",
"path": "m/44'/3'/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
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "getAccountAddresses",
"params": {
"account": "DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ"
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"address": "DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ",
"path": "m/44'/3'/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 Dogecoin 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": "DTyt9wHTgizR8CwK8HAsWDaoMMxcaRuLWJ",
"path": "m/44'/3'/0'/0/0"
},
{
"address": "DA6rZ9aV3mkz9uxNvddzzbXEEcSPN8SCUS",
"path": "m/44'/3'/0'/0/2"
},
{
"address": "DDtQfA541GQU2KDrY3ofF5F5hsKxkFiUuG",
"path": "m/44'/3'/0'/0/3"
},
{
"address": "D5A6wPFhCNChUiQHGXftD8DiNgc2G7yT1L",
"path": "m/44'/3'/0'/0/4"
},
{
"address": "DFG9R8ENG4mK5gUiU1VRr3FBT13LfWJ4Fb",
"path": "m/44'/3'/0'/1/3"
},
{
"address": "D7rakaGgZvaBH1vGTxnsQ3ZdV7ejX57hRy",
"path": "m/44'/3'/0'/1/4"
}
]
},
"chainId": "bip122:1a91e3dace36e2be3bf030a65679fe821"
}
}
```
# Ethereum
Source: https://docs.reown.com/advanced/multichain/rpc-reference/ethereum-rpc
Ethereum JSON-RPC Methods
## 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
// 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
// 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
[
"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
// 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://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)
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
[
{
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](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt) to get the contract address, after the transaction was mined, when you created a contract.
### Example
```javascript
// 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://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI)
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
[
{
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
// 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](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactionreceipt) to get the contract address, after the transaction was mined, when you created a contract.
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params":[
"0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f07244567"
],
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"
}
```
# WC JSON-RPC for Everscale
Source: https://docs.reown.com/advanced/multichain/rpc-reference/everscale-rpc
Everscale JSON-RPC Methods
### ever\_sign
Signature of a random message for authentication.
##### Parameters:
1. message - some message in base64;
2. withSignatureId - bool or number
3. hashData - bool
##### Returns:
1. signature - string signature in base64;
2. pubkey - public key of the wallet's address;
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_sign",
"params": {
"message": "some message in base64",
"withSignatureId": true,
"hashData": true
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"signature": "0xa3f207... in base64",
"pubkey": "0x07bf94e...37e3"
}
}
```
### ever\_sendMessage
Creates message, sends it to the network, monitors its processing and returns transaction's id.
##### Parameters:
1. value - amount of coins attached to the message;
2. bounce - should the answer message be generated in case of an error;
3. destAddress - message destination address;
4. destPayload - message destination payload;
4.1. abi - destination contract ABI;
4.2. method - destination contract method;
4.3. params - destination contract method params
##### Returns:
1. txId - transaction's id in blockchain;
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_sendMessage",
"params": {
"source_address": "0:695e42...b8d",
"value": 1000000000,
"bounce": False,
"destAddress": "0:b38d96...708",
"destPayload": {
"abi": "",
"method": "",
"params": {}
},
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"txId": "743e1c0046b82a48a2cf8cbe9a2059ce6f3862cfae377c77d9f1b4efd88d7acb"
}
}
```
### ever\_signMessage
Message that can then be sent to the blockchain.
##### Parameters:
1. value - amount of coins attached to the message;
2. bounce - should the answer message be generated in case of an error;
3. destAddress - message destination address;
4. destPayload - message destination payload;
4.1. abi - destination contract ABI;
4.2. method - destination contract method;
4.3. params - destination contract method params
##### Returns:
1. signedExtMessage - signed external message;
2. expireAt - message expiration timestamp
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_processMessage",
"params": {
"source_address": "0:695e42...b8d",
"value": 1000000000,
"bounce": False,
"destAddress": "0:b38d96...708",
"destPayload": {
"abi": "",
"method": "",
"params": {}
},
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"signedExtMessage": "c0b0996a9f0ea8e472041857ff2da9cf8086a78603f823a7170891f43a217ff1",
"expireAt": 1685594678
}
}
```
### ever\_sendExternalMessage
Sends an external message to the contract.
##### Parameters:
1. destAddress - message destination address;
2. destPayload - message destination payload;
2.1. abi - destination contract ABI;
2.2. method - destination contract method;
2.3. params - destination contract method params
##### Returns:
1. txId - transaction's id in blockchain;
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_sendExternalMessage",
"params": {
"sourceAddress": "0:695e42...b8d",
"destAddress": "0:b38d96...708",
"destPayload": {
"abi": "",
"method": "",
"params": {}
},
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"txId": "743e1c0046b82a48a2cf8cbe9a2059ce6f3862cfae377c77d9f1b4efd88d7acb"
}
};
```
### ever\_broadcastMessage
Sends an internal message from the user account without waiting for the transaction.
##### Parameters:
1. value - amount of coins attached to the message;
2. bounce - should the answer message be generated in case of an error;
3. destAddress - message destination address;
4. destPayload - message destination payload;
4.1. abi - destination contract ABI;
4.2. method - destination contract method;
4.3. params - destination contract method params
##### Returns:
1. hash: string - external message hash;
2. account: string - destination account address (equals to source\_address);
3. expireAt: number - message expiration timestamp
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_broadcastMessage",
"params": {
"sourceAddress": "0:695e42...b8d",
"value": 1000000000,
"bounce": False,
"destAddress": "0:b38d96...708",
"destPayload": {
"abi": "",
"method": "",
"params": {}
},
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"hash": "743e1c0046b82a48a2cf8cbe9a2059ce6f3862cfae377c77d9f1b4efd88d7acb",
"account": "0:695e42...b8d",
"expireAt": 1684327417543
}
};
```
### ever\_broadcastExternalMessage
Sends an external message to the contract without waiting for the transaction.
##### Parameters:
1. destAddress - message destination address;
2. destPayload - message destination payload;
2.1. abi - destination contract ABI;
2.2. method - destination contract method;
2.3. params - destination contract method params
##### Returns:
1. hash: string - external message hash;
2. account: string - destination account address (equals to source\_address);
3. expireAt: number - message expiration timestamp
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_broadcastExternalMessage",
"params": {
"sourceAddress": "0:695e42...b8d",
"destAddress": "0:b38d96...708",
"destPayload": {
"abi": "",
"method": "",
"params": {}
},
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"hash": "743e1c0046b82a48a2cf8cbe9a2059ce6f3862cfae377c77d9f1b4efd88d7acb",
"account": "0:b38d96...708",
"expireAt": 1684327417543
}
}
```
### ever\_addTokenAsset
Adds asset (TIP-3 or native tokens) to the selected account.
##### Parameters:
1. rootContract - asset root address. Note: you can add native coin by leaving this field empty
##### Returns:
1. newAsset: bool - returns true if the account did not have this asset before
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_addTokenAsset",
"params": {
"sourceAddress": "0:695e42...b8d",
"rootContract": "0:b38d96...708"
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"newAsset": true
}
}
```
### ever\_encryptData
Encrypts arbitrary data with specified algorithm for each specified recipient.
##### Parameters:
1. recipientPublicKeys - public keys of recipients. Hex encoded;
2. algorithm - encryption algorithm. Currently supports only “ChaCha20Poly1305”
3. data - base64 encoded data;
##### Returns:
1. encryptedData - encrypted data for each recipient
1.1. algorithm - encryption algorithm
1.2. sourcePublicKey - hex encoded encryptor's public key
1.3. recipientPublicKey - hex encoded recipient public key
1.4. data - base64 encoded data
1.5. nonce - base64 encoded nonce
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_encryptData",
"params": {
"sourceAddress": "0:695e42...b8d",
"recipientPublicKeys": ["0x6e74...4e95", "0x9532...403f"],
"algorithm": "ChaCha20Poly1305",
"data": "ZGF0YQ=="
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"encryptedData": [
{
"algorithm": "ChaCha20Poly1305",
"sourcePublicKey": "0x209f...d08f",
"recipientPublicKey": "0x6e74...4e95",
"data": "ZW5jRGF0YTE=",
"nonce": "bm9uY2Ux"
},
{
"algorithm": "ChaCha20Poly1305",
"sourcePublicKey": "0x209f...d08f",
"recipientPublicKey": "0x6e74...4e95",
"data":"ZW5jRGF0YTI=",
"nonce":"bm9uY2Uy"
}
]
}
};
```
### ever\_decryptData
Decrypts encrypted data.
##### Parameters:
1. algorithm - encryption algorithm. Currently supports only “ChaCha20Poly1305”
2. recipientPublicKey - hex encoded recipient public key
3. data - base64 encoded data
4. nonce - base64 encoded nonce
##### Returns:
1. data - base64 encoded decrypted data
##### Example:
```
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "ever_decryptData",
"params": {
"sourceAddress": "0:695e42...b8d",
"algorithm": "ChaCha20Poly1305",
"recipientPublicKey": "0x6e74...4e95",
"data": "ZW5jRGF0YTE=",
"nonce": "bm9uY2Ux"
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"data": "ZGF0YQ=="
}
};
```
# Hedera
Source: https://docs.reown.com/advanced/multichain/rpc-reference/hedera-rpc
Hedera JSON-RPC Methods
The following JSON-RPC methods offer native integration into Hedera utilizing the [Hedera APIs](https://hashgraph.github.io/hedera-protobufs/) and the [Hedera SDKs](https://docs.hedera.com/hedera/sdks-and-apis/sdks).
Hedera documentation can be found at [docs.hedera.com](https://docs.hedera.com/hedera/).
The following resources provide specific information referenced in the methods below.
* The Hedera network structure is summarized by [Mainnet Nodes](https://docs.hedera.com/hedera/networks/mainnet/mainnet-nodes)
* The full list of Hedera functionality is described by the protobuf definitions: [Hedera Functionality](https://hashgraph.github.io/hedera-protobufs/#proto.HederaFunctionality)
* Further details about these methods can be found in the accompanying Hedera Improvement Proposal: [HIP-820](https://hips.hedera.com/hip/hip-820)
* The `signerAccountId` utilized in the methods below is specified by [HIP-30](https://hips.hedera.com/hip/hip-30)
* A Hedera Transaction ID is composed of the account id that pays for a transaction and the valid start timestamp in nanoseconds: [Hedera Transaction ID](https://docs.hedera.com/hedera/sdks-and-apis/sdks/transactions/transaction-id)
* There are pre-processing validation response codes returned by the network: [ResponseCodeEnum](https://github.com/hashgraph/hedera-protobufs/blob/f36e05bd6bf3f572707ca9bb338f5ad6421a4241/services/response_code.proto#L32)
*Hedera has a separate open-source project implementing parts of the [Ethereum JSON-RPC standard](https://docs.hedera.com/hedera/core-concepts/smart-contracts/json-rpc-relay) which is not covered in this documentation.*
## Methods
* [`hedera_signAndExecuteTransaction`](#hedera_signandexecutetransaction)
* [`hedera_signTransaction`](#hedera_signtransaction)
* [`hedera_executeTransaction`](#hedera_executetransaction)
* [`hedera_signAndExecuteQuery`](#hedera_signandexecutequery)
* [`hedera_signMessage`](#hedera_signmessage)
* [`hedera_getNodeAddresses`](#hedera_getnodeaddresses)
## hedera\_signAndExecuteTransaction
The `hedera_signAndExecuteTransaction` method is a generic method for executing a transaction on the Hedera network. A dApp can begin by constructing a transaction with one of the Hedera SDKs or by constructing the raw protobuf messages and may select one or more consensus nodes that are authorized to execute the transaction.
The dApp then constructs a list of valid transaction bytes that differ only in the node account id and serializes the list, for example by using the `toBytes()` method of an SDK. Finally, the dApp base64 encodes the resulting bytes. This final base64 encoded string of bytes is sent as a method param titled `transactionList` to the wallet.
Wallets and SDKs must take special care to deserialize the list of transactions and validate that each transaction in the list differs only in the node authorized to receive the transaction and does NOT differ in intent before submitting to an end user for approval and ultimately signing.
### Parameters
1. `Object` - signAndExecuteTransaction parameters
1.1. `signerAccountId` : `String` - Hedera account id in the format `:..<-optional-checksum>`
1.2. `transactionList` : `String` - Base64 encoded string of TransactionList bytes
### Returns
1. `Object` - Result of transaction submission to Hedera network
1.1. `nodeId` : `String` - The Hedera node the transaction was submitted to
1.2. `transactionHash` : `String` - The hash of the transaction
1.3. `transactionId` : `String` - Transaction ID, which includes the payer account id and the valid start timestamp
### Error
In certain conditions, the Hedera network will return a response that signifies a pre-processing validation error, for example, when the transaction has expired. In these cases, wallets will return an error with the following format:
1. `Object` - Result of transaction submission to Hedera network
1.1. `code` : 9000 - The reserved WalletConnect error code for unknown errors or errors not related to the WalletConnect protocol
1.2. `message` : `String` - A human readable string describing the nature of the failure
1.3. `data` : `Number` - An integer representing the ResponseCodeEnum value returned from the Hedera Node, which indicates the reason for the failure
### Example
#### Request
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "hedera_signAndExecuteTransaction",
"params": {
"signerAccountId": "hedera:testnet:0.0.12345",
"transactionList": "Co8BKowBCocBChoKDAjchKWmBhDW..."
}
}
```
#### Result
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"nodeId": "0.0.3",
"transactionHash": "252b8fd...",
"transactionId": "0.0.12345678@1689281510.675369303"
}
}
```
#### Error
```json
{
"id": 1,
"jsonrpc": "2.0",
"error": {
"code": 9000,
"message": "The transaction failed with precheck code...",
"data": 6
}
}
```
## hedera\_signTransaction
The `hedera_signTransaction` signs a `TransactionBody` and returns a `SignatureMap` to the caller.
### Parameters
1. `Object` - signTransaction parameters
1.1 `signerAccountId` : `String` - Hedera account id in the format `:..<-optional-checksum>`
1.2 `transactionBody` : `String` - Base64 encoded string representation of TransactionBody
### Returns
1. `Object` - SignatureMap of related signed TransactionBody
1.1 `signatureMap` : `String` - Base64 encoded string of SignatureMap
### Example
#### Request
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "hedera_signTransaction",
"params": {
"signerAccountId": "hedera:testnet:0.0.12345",
"transactionBody": "Co8BKowBCocBChoKDAjchKWmBhDW..."
}
}
```
#### Result
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"signatureMap": "VGhpcyBpcyBqdXN0IHNvbWUgc3R1..."
}
}
```
## hedera\_executeTransaction
When a dApp only requires the services of the controller to act as a relay to the Hedera network for submitting an already signed transaction, it can use the `hedera_executeTransaction` method.
### Parameters
1. `Object` - executeTransaction parameters
1.1 `transactionList` : `String` Base64 encoded TransactionList
### Returns
1. `Object` - Result of transaction submission to the Hedera network
1.1. `nodeId` : `String` - The Hedera node the transaction was submitted to
1.1. `transactionHash` : `String` - The hash of the transaction
1.1. `transactionId` : `String` - Transaction ID, which includes the payer account id and the valid start timestamp
### Error
In certain conditions, the Hedera network with return a response that signifies a pre-processing validation error, for example, when the transaction has expired. In these cases, wallets will return an error with the following format:
1. `Object` - Result of transaction submission to the Hedera network
1.1. `code` : 9000 - The reserved WalletConnect error code for unknown errors or errors not related to the WalletConnect protocol
1.1. `message` : `String` - A human readable string describing the nature of the failure
1.1. `data` : `Number` - An integer representing the ResponseCodeEnum value returned from the Hedera Node, which indicates the reason for the failure
### Example
#### Request
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "hedera_executeTransaction",
"params": {
"transactionList": "Co8BKowBCocBChoKDAjchKWmBhDW..."
}
}
```
#### Result
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"nodeId": "0.0.3",
"transactionHash": "252b8fd...",
"transactionId": "0.0.12345678@1689281510.675369303"
}
}
```
#### Error
```json
{
"id": 1,
"jsonrpc": "2.0",
"error": {
"code": 9000,
"message": "The transaction failed with precheck code...",
"data": 6
}
}
```
## hedera\_signAndExecuteQuery
This method provides functionality to perform a query on a Hedera consensus node. Many Queries against consensus nodes have a transaction fee [Learn more about Queries](https://docs.hedera.com/hedera/sdks-and-apis/sdks/queries).
Most requests that do not change network state can be performed against a [Mirror Node](https://docs.hedera.com/hedera/core-concepts/mirror-nodes) and for many use cases calling a mirror node endpoint is the recommended approach.
### Parameters
1. `Object` - signAndExecuteQuery parameters
1.1 `signerAccountId` : `String` - Hedera account id in the format `:..<-optional-checksum>`
1.2 `query` : `String` - base64 encoded Query
### Returns
1. `Object` - Result of the Query submitted to a Hedera consensus node
1.1. `response` : `String` - Base64 encoding of the Hedera API response
## hedera\_signMessage
This method accepts a plain text string value as input. If approved by the user, the controller UTF-8 encodes this message prepended with "\x19Hedera Signed Message:\n" plus the length of the message and signs the resulting bytes in the same manner as HAPI transactions are signed. The resulting signature(s) are transmitted back to the user encoded in a SignatureMap structure. The pseudo code for computing the signature is as follows:
```javascript
.sign("\x19Hedera Signed Message:\n" + len(message) + message)
```
### Parameters
1. `Object` - signMessage parameters
1.1 `signerAccountId` : `String` - Hedera account id in the format `:..<-optional-checksum>`
1.2 `message` : `String`
### Returns
1. `Object` - signMessage result
1.1 `signatureMap` : `String` - Base64 encoded SignatureMap
### Example
#### Request
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "hedera_signMessage",
"params": {
"signerAccountId": "hedera:testnet:0.0.12345"
"message": "Co8BKowBCocBChoKDAjchKWmBhDW..."
}
}
```
#### Result
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"signatureMap": "CAAQABjMrxoYABIGCAAQABgHGIDIr..."
}
}
```
## hedera\_getNodeAddresses
While constructing a transaction for transmission to a controller, a dApp needs to choose which Hedera Network node shall receive the transaction prior to signing (this is a requirement of the Hedera API Protocol). While a dApp can easily obtain a list of potential Hedera Nodes, a controller may not have an all-inclusive list nor a path to the node’s gRPC endpoint. The `hedera_getNodeAddresses` method allows a dApp to request a list of node wallet addresses known to the controller. The controller should only include nodes in this list that it is willing and able to submit transactions to at the time of the request.
### Returns
1. `Object` - signMessage result
1.1 `nodes` : `Array` - an array of strings in `..<-optional-checksum>` format, each identifying a node by its Hedera Address
### Example
#### Request
```json
{
"id": 1,
"jsonrpc": "2.0",
"method": "hedera_getNodeAddresses"
}
```
#### Result
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"nodes": ["0.0.3", "0.0.4"]
}
}
```
# Litecoin
Source: https://docs.reown.com/advanced/multichain/rpc-reference/litecoin-rpc
Litecoin JSON-RPC Methods
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'/2'/0'/0/0` we get account 0 with identifier `ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0`. Its total balance is the sum of all UTXO balances on all addresses with derivation paths:
* `m/44'/2'/0'/change/address_index`
* `m/49'/2'/0'/change/address_index`
* `m/84'/2'/0'/change/address_index`
* `m/86'/2'/0'/change/address_index`
If the wallet user changes to account 1 we get path `m/84'/2'/1'/0/0` with identifier `ltc1qn9h77dt0s6ar78ptxq58t2ne7tyhvfnruc3e7d`. Its total balance is the sum of all UTXO balances on all addresses with derivation paths:
* `m/44'/2'/1'/change/address_index`
* `m/49'/2'/1'/change/address_index`
* `m/84'/2'/1'/change/address_index`
* `m/86'/2'/1'/change/address_index`
## sendTransfer
This method is used to sign and submit a transfer of any `amount` of Litecoin to a single `recipientAddress`, optionally including a `changeAddress` for the change amount and `memo` set as the OP\_RETURN value 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 Litecoin to send, denominated in litoshis (Litecoin 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` - The transaction id as a hex string without 0x prefix.
### Example
The example below specifies a simple transfer of 1.23 LTC (123000000 Litoshi).
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "sendTransfer",
"params": {
"account": "ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0",
"recipient": "ltc1qn9h77dt0s6ar78ptxq58t2ne7tyhvfnruc3e7d",
"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'/2'/0'/0/0".
* `intention` : `String` - *(Optional)* Intention of the address, e.g. "payment" or "ordinal".
### 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'/2'/0'/0/7` and `m/84'/2'/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
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "getAccountAddresses",
"params": {
"account": "ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0"
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"address": "ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0",
"path": "m/84'/2'/0'/0/0"
},
{
"address": "LXkGhTKmZpviAtYdDaxWbiJsdg4tA6EzrU",
"path": "m/49'/2'/0'/0/7"
},
{
"address": "ltc1qj4plcuyhuzw0sycf99gcayzhhcddfj6xkcke5g",
"path": "m/84'/2'/0'/0/2"
},
{
"address": "ltc1qsdxa6pseqekqg5d3uksaxnwrey2s2ujcx03alc",
"path": "m/84'/2'/0'/0/3"
},
{
"address": "ltc1qhuvt3sq8xmx9ktzdfznkzvjl5zup7mg9zpwllw",
"path": "m/84'/2'/0'/0/4"
},
{
"address": "ltc1qtjd3y5a2axpwzfjcj4y9zy50qfjuxwzm0vu5fq",
"path": "m/84'/2'/0'/1/3"
},
{
"address": "ltc1qp7ujtprgl0quvcg0dj335p37r2mc2cxdc8xumq",
"path": "m/84'/2'/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
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "getAccountAddresses",
"params": {
"account": "ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0"
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"address": "ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0",
"path": "m/84'/2'/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 Litecoin 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": "ltc1q8c6fshw2dlwun7ekn9qwf37cu2rn755u9ym7p0",
"path": "m/84'/2'/0'/0/0"
},
{
"address": "LXkGhTKmZpviAtYdDaxWbiJsdg4tA6EzrU",
"path": "m/49'/2'/0'/0/7"
},
{
"address": "ltc1qj4plcuyhuzw0sycf99gcayzhhcddfj6xkcke5g",
"path": "m/84'/2'/0'/0/2"
},
{
"address": "ltc1qsdxa6pseqekqg5d3uksaxnwrey2s2ujcx03alc",
"path": "m/84'/2'/0'/0/3"
},
{
"address": "ltc1qhuvt3sq8xmx9ktzdfznkzvjl5zup7mg9zpwllw",
"path": "m/84'/2'/0'/0/4"
},
{
"address": "ltc1qtjd3y5a2axpwzfjcj4y9zy50qfjuxwzm0vu5fq",
"path": "m/84'/2'/0'/1/3"
},
{
"address": "ltc1qp7ujtprgl0quvcg0dj335p37r2mc2cxdc8xumq",
"path": "m/84'/2'/0'/1/4"
}
]
},
"chainId": "bip122:12a765e31ffd4059bada1e25190f6e98"
}
}
```
# NEAR
Source: https://docs.reown.com/advanced/multichain/rpc-reference/near-rpc
NEAR JSON-RPC Methods
The method names below are based on the [Bridge Wallets](https://github.com/near/NEPs/blob/master/specs/Standards/Wallets/BridgeWallets.md#) Standard for NEAR.
## near\_getAccounts
Retrieve all accounts visible to the session. `publicKey` references the underlying `FullAccess` key linked to each account.
### Parameters
none
### Returns
1.`Array` - Array of accounts:
1.1. `Object`
1.1.1. `accountId` : `String` - The account name to which the publicKey corresponds as plain text
1.1.2. `publicKey` : `String` - The public counterpart of the key used to sign, expressed as a string with format `:`
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "near_getAccounts",
"params": {}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [{
"accountId": "alice.near",
"publicKey": "ed25519:DmnRVNb89cLKZY1cH1Zcr3rxMVD9r1cVfnDac7RFwM94"
}]
}
```
## near\_signIn
For dApps that often sign gas-only transactions, `FunctionCall` access keys can be created for one or more accounts to greatly improve the UX. While this could be achieved with `signTransactions`, it suggests a direct intention that a user wishes to sign in to a dApp's smart contract.
### Parameters
1. `Object` - Sign In parameters:
1.1. `permission` : `Object` - Function call key permission parameters
1.1.1. `receiverId` : `String` - smart contract for which the function call access key will be created
1.1.2. `methodNames` : `Array` - list of methods that can be called on the smart contract
1.2. `accounts` : `Array` - list of accounts for which a FunctionCall access key will be added:
1.2.1. `Object` - Account
1.2.1.1. `accountId` : `String` - The account name to which the publicKey corresponds as plain text
1.2.1.2. `publicKey` : `String` - The public counterpart of the key used to sign, expressed as a string with format `:`
### Returns
void
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "near_signIn",
"params": {
"permission": {
"receiverId": "guest-book.testnet",
"methodNames": []
},
"accounts": [{
"accountId": "alice.near",
"publicKey": "ed25519:DmnRVNb89cLKZY1cH1Zcr3rxMVD9r1cVfnDac7RFwM94"
}]
}
}
```
## near\_signOut
Delete one or more `FunctionCall` access keys created with `signIn`. While this could be achieved with `signTransactions`, it suggests a direct intention that a user wishes to sign out from a dApp's smart contract.
### Parameters
1.`Array` - Array of accounts:
1.1. `Object`
1.1.1. `accountId` : `String` - The account name to which the publicKey corresponds as plain text
1.1.2. `publicKey` : `String` - The public counterpart of the key used to sign, expressed as a string with format `:`
### Returns
void
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "near_signOut",
"params": {
"accounts": [{
"accountId": "alice.near",
"publicKey": "ed25519:DmnRVNb89cLKZY1cH1Zcr3rxMVD9r1cVfnDac7RFwM94"
}]
}
}
```
## near\_signTransaction
Sign a transaction. It makes use of [near-api-js](https://github.com/near/near-api-js) to enable interoperability with dApps that will already use it for constructing transactions and communicating with RPC endpoints.
[Transaction](https://nomicon.io/RuntimeSpec/Transactions) passed to `signTransaction` must be encoded.
### Parameters
1. `Object` - Signing parameters:
1.1. `transaction` : `Uint8Array` - Encoded Transaction via transactions.Transaction.encode()
### Returns
The result of `signTransaction` and is encoded [SignedTransaction](https://nomicon.io/RuntimeSpec/Transactions#signed-transaction) model.
1. `Uint8Array` - Encoded SignedTransaction via transactions.SignedTransaction.encode()
### Example
```javascript
// Request
{
"id": 1,
"jsonrpc": "2.0",
"method": "near_signTransaction",
"params": {
"transaction": {
"type": "Buffer",
"data": [32, 0, 0, 0, 100, 101, 118, 45, 49, 54, 55, 49, 51, 56, 55, 51, 57, 56 ...]
}
}
}
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"type": "Buffer",
"data": [32, 0, 0, 0, 100, 101, 118, 45, 49, 54, 55, 49, 51, 56, 55, 51, 57, 56 ...]
}
}
```
## near\_signTransactions
Sign a list of transactions. It makes use of [near-api-js](https://github.com/near/near-api-js) to enable interoperability with dApps that will already use it for constructing transactions and communicating with RPC endpoints.
[Transactions](https://nomicon.io/RuntimeSpec/Transactions) passed to `signTransactions` must be encoded.
### Parameters
1. `Array