Usage
This section provides instructions on how to initialize the Web3Wallet client, approve sessions with supported namespaces, and respond to session requests, enabling easy integration of Web3 wallets with dapps through a simple and intuitive interface.
Initialization
- Web
- iOS
- Android
- Flutter
- React Native
- C#
Create a new instance from Core and initialize it with a projectId created from installation. Next, create web3Wallet instance by calling init on Web3Wallet. Passing in the options object containing metadata about the app and an optional relay URL.
import { Core } from '@walletconnect/core'
import { Web3Wallet } from '@walletconnect/web3wallet'
const core = new Core({
projectId: process.env.PROJECT_ID
})
const web3wallet = await Web3Wallet.init({
core, // <- pass the shared `core` instance
metadata: {
name: 'Demo app',
description: 'Demo Client as Wallet/Peer',
url: 'www.walletconnect.com',
icons: []
}
})
Confirm you have configured the Network Client first.
Once you're done, in order to initialize a client just call a configure
method from the Web3Wallet instance wrapper
let metadata = AppMetadata(
name: "Example Wallet",
description: "Wallet description",
url: "example.wallet",
icons: ["https://avatars.githubusercontent.com/u/37784886"]
)
Web3Wallet.configure(
metadata: metadata,
crypto: DefaultCryptoProvider(),
// Used for the Echo: "echo.walletconnect.com" will be used by default if not provided
echoHost: "echo.walletconnect.com",
// Used for the Echo: "APNSEnvironment.production" will be used by default if not provided
environment: .production
)
In order to allow users to receive push notifications you have to communicate with Apple Push Notification service and receive unique device token. Register that token with following method:
try await Web3Wallet.instance.registerEchoClient(deviceToken: deviceToken)
val projectId = "" // Get Project ID at https://cloud.walletconnect.com/
val relayUrl = "relay.walletconnect.com"
val serverUrl = "wss://$relayUrl?projectId=$projectId"
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(relayServerUrl = serverUrl, connectionType = connectionType, application = this, metaData = appMetaData)
val initParams = Wallet.Params.Init(core = CoreClient)
Web3Wallet.initialize(initParams) { error ->
// Error will be thrown if there's an issue during initialization
}
The Web3Wallet client will always be responsible for exposing accounts (CAIP10 compatible) to a Dapp and therefore is also in charge of signing.
To initialize the Web3Wallet client, create a Wallet.Params.Init
object in the Android Application class with the Core Client. The Wallet.Params.Init
object will then be passed to the Web3Wallet
initialize function.
To create an instance of Web3Wallet, you need to pass in the core
and metadata
parameters.
Web3Wallet web3Wallet = await Web3Wallet.createInstance(
relayUrl: 'wss://relay.walletconnect.com', // The relay websocket URL, leave blank to use the default
projectId: '123',
metadata: PairingMetadata(
name: 'Wallet (Responder)',
description: 'A wallet that can be requested to sign transactions',
url: 'https://walletconnect.com',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
),
);
Create a new instance from Core
and initialize it with your projectId
. Next, create a Web3Wallet instance by calling init
on Web3Wallet
. Passing in the options object containing metadata about the app.
In this code example, we wrapped it in a createWeb3Wallet
function as this will be easier to call from your App.tsx
or an initialization function as seen here.
The pair
function will help us pair between the dapp and wallet and will be used shortly.
import { Core } from '@walletconnect/core'
// import { ICore } from '@walletconnect/types' <- Add if using TS
import { Web3Wallet, IWeb3Wallet } from '@walletconnect/web3wallet'
// export let web3wallet: IWeb3Wallet <- Add if using TS
// export let core: ICore <- Add if using TS
const core = new Core({
projectId: process.env.PROJECT_ID
})
export async function createWeb3Wallet() {
const web3wallet = await Web3Wallet.init({
core, // <- pass the shared `core` instance
metadata: {
name: 'Demo React Native Wallet',
description: 'Demo RN Wallet to interface with Dapps',
url: 'www.walletconnect.com',
icons: []
}
})
}
export async function pair(params: { uri: string }) {
return await core.pairing.pair({ uri: params.uri })
}
First you must setup a WalletConnectCore
instance with a specific Name
and ProjectId
. You may optionally specify other CoreOption
values, such as RelayUrl
and Storage
var options = new CoreOptions()
{
ProjectId = "...",
Name = "my-app",
}
var core = new WalletConnectCore(options);
Next, you must define a Metadata
object which describes your Wallet. This includes a Name
, Description
, Url
and Icons
url.
var metadata = new Metadata()
{
Description = "An example wallet to showcase WalletConnectSharpv2",
Icons = new[] { "https://walletconnect.com/meta/favicon.ico" },
Name = $"wallet-csharp-test",
Url = "https://walletconnect.com",
};
Once you have both the WalletConnectCore
and Metadata
objects, you can initialize the Web3WalletClient
var sdk = await Web3WalletClient.Init(core, metadata, metadata.Name);
Session
A session is a connection between a dapp and a wallet. It is established when a user approves a session proposal from a dapp. A session is active until the user disconnects from the dapp or the session expires.
Namespace Builder
- Web
- iOS
- Android
- Flutter
- React Native
- C#
With Web3Wallet v1.5.1 (and @walletconnect/utils v2.6.1) we've published a helper utility that greatly reduces the complexity of parsing the required
and optional
namespaces. It accepts as parameters a session proposal
along with your user's chains/methods/events/accounts
and returns ready-to-use namespaces
object.
// util params
{
proposal: ProposalTypes.Struct; // the proposal received by `.on("session_proposal")`
supportedNamespaces: Record< // your Wallet's supported namespaces
string, // the supported namespace key e.g. eip155
{
chains: string[]; // your supported chains in CAIP-2 format e.g. ["eip155:1", "eip155:2", ...]
methods: string[]; // your supported methods e.g. ["personal_sign", "eth_sendTransaction"]
events: string[]; // your supported events e.g. ["chainChanged", "accountsChanged"]
accounts: string[] // your user's accounts in CAIP-10 format e.g. ["eip155:1:0x453d506b1543dcA64f57Ce6e7Bb048466e85e228"]
}
>;
};
Example usage
// import the builder util
import { buildApprovedNamespaces } from '@walletconnect/utils'
web3wallet.on('session_proposal', async sessionProposal => {
const { id, params } = sessionProposal
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {
chains: ['eip155:1', 'eip155:137'],
methods: ['eth_sendTransaction', 'personal_sign'],
events: ['accountsChanged', 'chainChanged'],
accounts: [
'eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb',
'eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb'
]
}
}
})
// ------- end namespaces builder util ------------ //
const session = await web3wallet.approveSession({
id,
namespaces: approvedNamespaces
})
})
If your wallet supports multiple namespaces e.g. eip155
,cosmos
& near
Your supportedNamespaces
should look like the following example.
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {...},
cosmos: {...},
near: {...}
},
});
// ------- end namespaces builder util ------------ //
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.
public static func build(
sessionProposal: Session.Proposal,
chains: [Blockchain],
methods: [String],
events: [String],
accounts: [Account]
) throws -> [String: SessionNamespace]
Example usage
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 Web3Wallet.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces)
} catch {
print(error)
}
With Web3Wallet SDK 1.7.0 we've published a helper utility that greatly reduces the complexity of parsing the required and optional namespaces. It accepts as parameters a session proposal along with your wallet's chains, methods, events, and accounts (supported namespaces) and returns ready-to-use namespaces object that has to be passed into Wallet.Params.SessionApprove
when approving a session.
val supportedNamespaces: Wallet.Model.Namespaces.Session = /* a map of all supported namespaces created by a wallet */
val sessionProposal: Wallet.Model.SessionProposal = /* an object received by `fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal)` in `Web3Wallet.WalletDelegate` */
val sessionNamespaces = Web3Wallet.generateApprovedNamespaces(sessionProposal, supportedNamespaces)
val approveParams: Wallet.Params.SessionApprove = Wallet.Params.SessionApprove(proposerPublicKey, sessionNamespaces)
Web3Wallet.approveSession(approveParams) { error -> /*callback for error while approving a session*/ }
Examples of supported namespaces:
val supportedNamespaces = mapOf(
"eip155" to Wallet.Model.Namespace.Session(
chains = listOf("eip155:1", "eip155:137", "eip155:3"),
methods = listOf("personal_sign", "eth_sendTransaction", "eth_signTransaction"),
events = listOf("chainChanged"),
accounts = listOf("eip155:1:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:137:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:3:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092")
)
)
val anotherSupportedNamespaces = mapOf(
"eip155" to Wallet.Model.Namespace.Session(
chains = listOf("eip155:1", "eip155:2", "eip155:4"),
methods = listOf("personal_sign", "eth_sendTransaction", "eth_signTransaction"),
events = listOf("chainChanged", "accountChanged"),
accounts = listOf("eip155:1:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:2:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:4:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092")
),
"cosmos" to Wallet.Model.Namespace.Session(
chains = listOf("cosmos:cosmoshub-4"),
methods = listOf("cosmos_method"),
events = listOf("cosmos_event"),
accounts = listOf("cosmos:cosmoshub-4:cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")
)
)
With Web3Wallet v1.5.1 (and @walletconnect/utils v2.6.1) we've published a helper utility that greatly reduces the complexity of parsing the required
and optional
namespaces. It accepts as parameters a session proposal
along with your user's chains/methods/events/accounts
and returns ready-to-use namespaces
object.
// util params
{
proposal: ProposalTypes.Struct; // the proposal received by `.on("session_proposal")`
supportedNamespaces: Record< // your Wallet's supported namespaces
string, // the supported namespace key e.g. eip155
{
chains: string[]; // your supported chains in CAIP-2 format e.g. ["eip155:1", "eip155:2", ...]
methods: string[]; // your supported methods e.g. ["personal_sign", "eth_sendTransaction"]
events: string[]; // your supported events e.g. ["chainChanged", "accountsChanged"]
accounts: string[] // your user's accounts in CAIP-10 format e.g. ["eip155:1:0x453d506b1543dcA64f57Ce6e7Bb048466e85e228"]
}
>;
};
Example usage
// import the builder util
import { buildApprovedNamespaces } from '@walletconnect/utils'
web3wallet.on('session_proposal', async sessionProposal => {
const { id, params } = sessionProposal
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {
chains: ['eip155:1', 'eip155:137'],
methods: ['eth_sendTransaction', 'personal_sign'],
events: ['accountsChanged', 'chainChanged'],
accounts: [
'eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb',
'eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb'
]
}
}
})
// ------- end namespaces builder util ------------ //
const session = await web3wallet.approveSession({
id,
namespaces: approvedNamespaces
})
})
If your wallet supports multiple namespaces e.g. eip155
,cosmos
& near
Your supportedNamespaces
should look like the following example.
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {...},
cosmos: {...},
near: {...}
},
});
// ------- end namespaces builder util ------------ //
To build a namespace mapping for either proposing a session OR approving a session, you can use C# dictionary + class constructors directly, or use the built-in builder methods
C# Constructor Style
var TestNamespaces = new Namespaces()
{
{
"eip155", new Namespace()
{
Accounts = new [] { "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" },
Chains = new []{ "eip155:1" },
Methods = new[] { "eth_signTransaction" },
Events = new[] { "chainChanged" }
}
},
};
Builder Style
var TestNamespaces = new Namespaces()
.WithNamespace("eip155", new Namespace()
.WithChain("eip155:1")
.WithMethod("eth_signTransaction")
.WithEvent("chainChanged")
.WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")
);
The Namespaces
mapping is required when approving a proposed session from a dApp. Because of this, you may
also construct a Namespaces
from a RequiredNamespaces
, which auto-populates all Methods
, Events
and
Chains
from the given RequiredNamespaces
. This is provided for convenience.
RequiredNamespaces
sdk.On<SessionProposalEvent>(EngineEvents.SessionProposal, async (sender, @event) =>
{
var proposal = @event.EventData.Proposal;
var requiredNamespaces = proposal.RequiredNamespaces;
var approvedNamespaces = new Namespaces(requiredNamespaces);
approvedNamespaces["eip155"].WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb");
});
The RequiredNamespaces
is required when setting up a session between a dApp and Wallet. The
dApp will provide a RequiredNamespaces
when proposing the session. The RequiredNamespaces
and
ProposedNamespace
use the same style constructors + builder functions as Namespaces
and Namespace
.
Session Approval
- Web
- iOS
- Android
- Flutter
- React Native
- C#
The session_proposal
event is emitted when a dapp initiates a new session with a user's wallet. The event will include a proposal
object with information about the dapp and requested permissions. The wallet should display a prompt for the user to approve or reject the session. If approved, call approveSession
and pass in the proposal.id
and requested namespaces
.
The pair
method initiates a WalletConnect pairing process with a dapp using the given uri
(QR code from the dapps). To learn more about pairing, checkout out the docs.
web3wallet.on('session_proposal', async proposal => {
const session = await web3wallet.approveSession({
id: proposal.id,
namespaces
})
})
await web3wallet.pair({ uri })
🛠️ Usage examples
⚠️ Expected Errors
No matching key. proposal id doesn't exist: 1
This rejection means the SDK can't find a record with the given proposal.id
- in this example 1
.
This can happen when the proposal has expired (by default 5 minutes) or if you attempt to respond to a proposal that has already been approved/rejected.
If you are seeing this error, please make sure that you are calling approveSession
with the correct proposal.id
that is available whithin the proposal payload.
Error: Missing or invalid. approve(), namespaces should be an object with data
This error means that the namespaces
parameter passed to approveSession
is either missing or invalid. Please check that you are passing a valid namespaces
object that satisfies all required properties.
Non conforming namespaces. approve() namespaces <property> don't satisfy required namespaces.
This error indicates that some value(s) in your namespaces
object do not satisfy the required namespaces requested by the dapp.
To provide additional guidance, the message might include info about the exact property that is missing or invalid e.g. Required: eip155:1 Approved: eip155:137
.
Please check CAIP-25 to familiarize yourself with the standard and it's nuanses.
Additonaly, we highly recommend you to use our namespace
builder utility that would greatly simplify the process of parsing & building a valid namespaces
object.
Web3Wallet.instance.approve(
proposalId: "proposal_id",
namespaces: sessionNamespaces
)
When session is successfully approved sessionsPublishers
will publish a Session
Web3Wallet.instance.sessionsPublishers
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.reloadSessions()
}.store(in: &publishers)
Session
object represents an active session connection with a dapp. It contains dapp’s metadata (that you may want to use for displaying an active session to the user), namespaces, and expiry date. There is also a topic property that you will use for linking requests with related sessions.
You can always query settled sessions from the client later with:
Web3Wallet.instance.getSessions()
Connect Clients
Your Wallet should allow users to scan a QR code generated by dapps. You are responsible for implementing it on your own.
For testing, you can use our test dapp at: https://react-app.walletconnect.com/, which is v2 protocol compliant.
Once you derive a URI from the QR code call pair
method:
try await Web3Wallet.instance.pair(uri: uri)
if everything goes well, you should handle following event:
Web3Wallet.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.
Session.Proposal
object conveys set of required and optional ProposalNamespaces
that contains blockchains methods and events. Dapp requests with methods and wallet will emit events defined in namespaces.
VerifyContext
provides a domain verification information about Session.Proposal
and Request
. It consists of origin of a Dapp from where the request has been sent, validation enum that says whether origin is unknown, valid or invalid and verify URL server.
To enable or disable verification find the Verify SDK toggle in your project cloud.
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 specification and be prefixed with a chain identifier. chain_id + : + account_address. You can find more on blockchain identifiers in CAIP2. 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:
{
"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:
{
"eip155": {
"accounts": [
"eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb",
"eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"
],
"methods": ["eth_sign"],
"events": ["accountsChanged"]
},
"cosmos": {
"accounts": ["cosmos:cosmoshub-4:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0"],
"methods": ["cosmos_signDirect", "personal_sign"],
"events": ["someCosmosEvent", "proofFinalized"]
}
}
Track Sessions
When your Web3Wallet
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
Web3Wallet.instance.sessionsPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] sessions in
// Reload UI
}.store(in: &publishers)
Addresses provided in accounts
array should follow CAPI10
semantics.
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<String> = /*List of accounts on chains*/
val methods: List<String> = /*List of methods that wallet approves*/
val events: List<String> = /*List of events that wallet approves*/
val namespaces: Map<String, Wallet.Model.Namespaces.Session> = mapOf(namespace, Wallet.Model.Namespaces.Session(accounts, methods, events))
val approveParams: Wallet.Params.SessionApprove = Wallet.Params.SessionApprove(proposerPublicKey, namespaces)
Web3Wallet.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 Web3Wallet.approveSession
function.
Present the UI for approval.
final walletNamespaces = {
'eip155': Namespace(
accounts: ['eip155:1:abc'],
methods: ['eth_signTransaction'],
),
'kadena': Namespace(
accounts: ['kadena:mainnet01:abc'],
methods: ['kadena_sign_v1', 'kadena_quicksign_v1'],
events: ['kadena_transaction_updated'],
),
}
await web3Wallet.approve(
id: id,
namespaces: walletNamespaces // This will have the accounts requested in params
);
Pairing
Scan the QR code and parse the URI, and pair with the dapp.
Upon the first pairing, you will immediately receive onSessionProposal
and onAuthRequest
events.
Uri uri = Uri.parse(scannedUriString);
await web3Wallet.pair(uri: uri);
In order to connect with a dapp, you will need to receive a WalletConnect URI (WCURI) and this will talk to our protocol to facilitate a pairing session. Therefore, you will need a test dapp in order to communicate with the wallet. We recommend testing with our React V2 Dapp as this is the most up-to-date development site.
In order to capture the WCURI, recommend having some sort of state management you will pass through a TextInput
or QRcode instance.
The session_proposal
event is emitted when a dapp initiates a new session with a user's wallet. The event will include a proposal
object with information about the dapp and requested permissions. The wallet should display a prompt for the user to approve or reject the session. If approved, call approveSession
and pass in the proposal.id
and requested namespaces
.
The pair
method initiates a WalletConnect pairing process with a dapp using the given uri
(QR code from the dapps). To learn more about pairing, checkout out the docs.
import {getSdkError} from '@walletconnect/utils';
...
const [wcuri, setWCUri] = useState<string>();
// Approval: Using this listener for sessionProposal, you can accept the session
web3wallet.on("session_proposal", async (proposal) => {
const session = await web3wallet.approveSession({
id: proposal.id,
namespaces,
});
});
// Call this after WCURI is received
await web3wallet.pair({ wcuri });
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.
var uri = "...";
ProposalStruct proposal = await sdk.Pair(uri);
The wallet can then approve the proposal by constructing an approved Namespaces
. The approved
Namespaces
should include the RequiredNamespaces
under proposal.RequiredNamespaces
, and may optionally include any optional namespaces
specified under proposal.OptionalNamespaces
sdk.On<SessionProposalEvent>(EngineEvents.SessionProposal, async (sender, @event) =>
{
var proposal = @event.EventData.Proposal;
var requiredNamespaces = proposal.RequiredNamespaces;
var approvedNamespaces = new Namespaces(requiredNamespaces);
approvedNamespaces["eip155"].WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb");
var sessionData = await sdk.ApproveSession(proposal.Id, approvedNamespaces);
var sessionTopic = sessionData.Topic;
});
You may also just provide the addresses that will connect, and the SDK will create this approved
Namespaces
for you. This function will not approve optional namespaces
sdk.On<SessionProposalEvent>(EngineEvents.SessionProposal, async (sender, @event) =>
{
var proposal = @event.EventData.Proposal;
var sessionData = await sdk.ApproveSession(proposal, new[] { "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" });
var sessionTopic = sessionData.Topic;
});
or
sdk.On<SessionProposalEvent>(EngineEvents.SessionProposal, async (sender, @event) =>
{
var proposal = @event.EventData.Proposal;
var sessionData = await sdk.ApproveSession(proposal, "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb");
var sessionTopic = sessionData.Topic;
});
Session Rejection
- Web
- iOS
- Android
- Flutter
- React Native
- C#
In the event you want to reject the session proposal, call the rejectSession
method. The getSDKError
function comes from the @walletconnect/utils
library.
web3wallet.on('session_proposal', async proposal => {
const session = await web3wallet.rejectSession({
id: proposal.id,
reason: getSdkError('USER_REJECTED_METHODS')
})
})
🛠️ Usage examples
⚠️ Expected Errors
No matching key. proposal id doesn't exist: 1
This rejection means the SDK can't find a record with the given proposal.id
- in this example 1
.
This can happen when the proposal has expired (by default 5 minutes) or if you attempt to respond to a proposal that has already been approved/rejected.
If you are seeing this error, please make sure that you are calling rejectSession
with the correct proposal.id
that is available whithin the proposal payload.
Error: Missing or invalid. reject() reason:
This rejection means the reason
parameter passed to rejectSession
is either missing or invalid.
We recommend using the getSDKError
function from the @walletconnect/utils
library that will populate & format the parameter for you.
val proposerPublicKey: String = /*Proposer publicKey from SessionProposal object*/
val rejectionReason: String = /*The reason for rejecting the Session Proposal*/
val rejectionCode: String = /*The code for rejecting the Session Proposal*/
For reference use CAIP-25: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md
val rejectParams: Wallet.Params.SessionReject = SessionReject(proposerPublicKey, rejectionReason, rejectionCode)
Web3Wallet.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 Web3Wallet.rejectSession
function.
To reject the request, pass in an error code and reason. They can be found here.
await web3Wallet.reject(
id: id,
reason: Errors.getSdkError(Errors.USER_REJECTED_SIGN),
);
You can use the getSDKError
function, which is available in the @walletconnect/utils
for the rejection function library.
import {getSdkError} from '@walletconnect/utils';
...
const [wcuri, setWCUri] = useState<string>();
// Reject: Using this listener for sessionProposal, you can reject the session
web3wallet.on("session_proposal", async (proposal) => {
const session = await web3wallet.rejectSession({
id: proposal.id,
reason: getSdkError("USER_REJECTED_METHODS"),
});
});
// Call this after WCURI is received
await web3wallet.core.pairing.pair({ wcuri });
The wallet can reject the proposal using the following:
sdk.On<SessionProposalEvent>(EngineEvents.SessionProposal, async (sender, @event) =>
{
var proposal = @event.EventData.Proposal;
await sdk.Reject(proposal, "User rejected");
});
Responding to Session requests
- Web
- iOS
- Android
- Flutter
- React Native
- C#
The session_request
event is emitted when the SDK received a request from the peer and it needs the wallet to perform a specific action, such as signing a transaction. The event contains a topic
and a request
object, which will vary depending on the action requested.
To respond to the request, you can access the topic
and request
object by destructuring them from the event payload. To see a list of possible request
and response
objects, refer to the relevant JSON-RPC Methods for Ethereum, Solana, Cosmos, or Stellar.
As an example, if the dapp requests a personal_sign
method, you can extract the params
array from the request
object. The first item in the array is the hex version of the message to be signed, which can be converted to UTF-8 and assigned to a message
variable. The second item in params
is the user's wallet address.
To sign the message, you can use your wallet's signMessage
method and pass in the message. The signed message, along with the id
from the event payload, can then be used to create a response
object, which can be passed into respondSessionRequest
.
web3wallet.on('session_request', async event => {
const { topic, params, id } = event
const { request } = params
const requestParamsMessage = request.params[0]
// convert `requestParamsMessage` by using a method like hexToUtf8
const message = hexToUtf8(requestParamsMessage)
// sign the message
const signedMessage = await wallet.signMessage(message)
const response = { id, result: signedMessage, jsonrpc: '2.0' }
await web3wallet.respondSessionRequest({ topic, response })
})
To reject a session request, the response should be similar to this.
const response = {
id,
jsonrpc: '2.0',
error: {
code: 5000,
message: 'User rejected.'
}
}
🛠️ Usage examples
⚠️ Expected Errors
Error: No matching key. session topic doesn't exist: 'xyz...'
This rejection means the SDK can't find a session with the given topic
- in this example xyz...
.
This can happen when the session has been disconnected by either the wallet or the dapp while the session request was being processed or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic that is available whithin the request payload.
Error: Missing or invalid. respond() response:
This rejection means the response
parameter passed to respondSessionRequest
is either missing or invalid. The response should be a valid JSON-RPC 2.0 response object.
We recommend you to use our formatJsonRpcResult
utility from "@walletconnect/jsonrpc-utils"
that will format the response for you.
Example usage:
id
argument being the request id from the request payload.
import { formatJsonRpcResult } from '@walletconnect/jsonrpc-utils'
const signature = await cryptoWallet.signTransaction(signTransaction)
const response = await web3wallet.respondSessionRequest({
topic: session.topic,
response: formatJsonRpcResult(id, signature)
})
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.
Web3Wallet.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:
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.
let response: AnyCodable = sign(request: sessionRequest) // Implement your signing method
try await Web3Wallet.instance.respond(topic: request.topic, requestId: request.id, response: .response(response))
val sessionTopic: String = /*Topic of Session*/
val jsonRpcResponse: Wallet.Model.JsonRpcResponse.JsonRpcResult = /*Active Session Request ID along with request data*/
val result = Wallet.Params.SessionRequestResponse(sessionTopic = sessionTopic, jsonRpcResponse = jsonRpcResponse)
Web3Wallet.respondSessionRequest(result) { error -> /*callback for error while responding session request*/ }
To respond to JSON-RPC method that were sent from Dapps for a session, submit a Wallet.Params.SessionRequestResponse
with the session's topic and request
ID along with the respond data to the Web3Wallet.respondSessionRequest
function.
Set up the proposal handler that will display the proposal to the user after the URI has been scanned.
late int id;
web3Wallet.onSessionProposal.subscribe((SessionProposal? args) async {
// Handle UI updates using the args.params
// Keep track of the args.id for the approval response
id = args!.id;
})
The session_request
event is triggered by a dapp when it needs the wallet to perform a specific action, such as signing a transaction. The event contains a topic
and a request
object, which will vary depending on the action requested.
To respond to the request, the wallet can access the topic
and request
object by destructuring them from the event payload. To see a list of possible request
and response
objects, refer to the relevant JSON-RPC Methods for Ethereum, Solana, Cosmos, or Stellar.
As an example, if the dapp requests a personal_sign
method, the wallet can extract the params
array from the request
object. The first item in the array is the hex version of the message to be signed, which can be converted to UTF-8 and assigned to a message
variable. The second item in params
is the user's wallet address.
To sign the message, the wallet can use the wallet.signMessage
method and pass in the message. The signed message, along with the id
from the event payload, can then be used to create a response
object, which can be passed into respondSessionRequest
.
The wallet then signs the message. signedMessage
, along with the id
from the event payload, can then be used to create a response
object, which can be passed into respondSessionRequest
.
web3wallet.on('session_request', async event => {
const { topic, params, id } = event
const { request } = params
const requestParamsMessage = request.params[0]
// convert `requestParamsMessage` by using a method like hexToUtf8
const message = hexToUtf8(requestParamsMessage)
// sign the message
const signedMessage = await wallet.signMessage(message)
const response = { id, result: signedMessage, jsonrpc: '2.0' }
await web3wallet.respondSessionRequest({ topic, response })
})
To reject a session request, the response should be similar to this.
const response = {
id,
jsonrpc: '2.0',
error: {
code: 5000,
message: 'User rejected.'
}
}
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 staticly 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<T>
to workaround this. For example, for eth_sendTransaction
, use List<Transaction>
instead of Transaction
.
Newtonsoft.Json is used for JSON serialization/deserialization, therefor 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
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:
[RpcMethod("eth_getTransactionReceipt"), RpcRequestOptions(Clock.ONE_MINUTE, 99994)]
public class EthGetTransactionReceipt : List<string>
{
public EthGetTransactionReceipt(params string[] hashes) : base(hashes)
{
}
// needed for proper json deserialization
public EthGetTransactionReceipt()
{
}
}
We can handle the eth_getTransactionReceipt
session request by doing the following:
walletClient.Engine.SessionRequestEvents<EthGetTransactionReceipt, TransactionReceipt>().OnRequest += OnEthTransactionReceiptRequest;
private Task OnEthTransactionReceiptRequest(RequestEventArgs<EthGetTransactionReceipt, TransactionReceipt> 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
walletClient.Engine.SessionRequestEvents<EthGetTransactionReceipt, TransactionReceipt>()
.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<T, TR>
with the desired response.
private async Task OnEthTransactionReceiptRequest(RequestEventArgs<EthGetTransactionReceipt, TransactionReceipt> e)
{
var txHash = e.Request.Params[0];
var receipt = await EthGetTransactionReceipt(txHash);
e.Response = receipt;
}
Updating a Session
- Web
- iOS
- Android
- Flutter
- React Native
- C#
An example adding a new account to an existing session:
const namespaces = session.namespaces
const acounts = [
'eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb',
'eip155:1:0x1234567890123456789012345678901234567890'
]
const updatedNamespaces = {
...namespaces,
eip155: {
...namespaces.eip155,
accounts
}
}
await web3wallet.updateSession({ topic: session.topic, namespaces: updatedNamespaces })
An example adding a new chain to an existing session:
const namespaces = session.namespaces
const chains = ['eip155:1', 'eip155:137']
const acounts = [
'eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb',
'eip155:137:0x1234567890123456789012345678901234567890'
]
const updatedNamespaces = {
...namespaces,
eip155: {
...namespaces.eip155,
accounts,
chains
}
}
await web3wallet.updateSession({ topic: session.topic, namespaces: updatedNamespaces })
🛠️ Usage examples
⚠️ Expected Errors
Note that all namespaces
validation applies and you still have to satisfy the required namespaces requested by the dapp.
Error: No matching key. session topic doesn't exist: 'xyz...'
This rejection means the SDK can't find a session with the given topic
- in this example xyz...
.
This can happen when the session you're trying to update has already been disconnected by either the wallet or the dapp or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic of an active session.
Error: Missing or invalid. update(), namespaces should be an object with data
This error means that the namespaces
parameter passed to updateSession
is either missing or invalid. Please check that you are passing a valid namespaces
object that satisfies all required properties.
Non conforming namespaces. update() namespaces <property> don't satisfy required namespaces.
This error indicates that some value(s) in your namespaces
object do not satisfy the required namespaces requested by the dapp.
To provide additional guidance, the message might include info about the exact property that is missing or invalid e.g. Required: eip155:1 Approved: eip155:137
.
Please check CAIP-25 to familiarize yourself with the standard and it's nuanses.
Additonaly, we highly recommend you to use our namespace
builder utility that would greatly simplify the process of parsing & building a valid namespaces
object.
If you want to update user session's chains, accounts, methods or events you can use session update method.
try await Web3Wallet.instance.update(topic: session.topic, namespaces: newNamespaces)
NOTE: addresses provided in accounts
array should follow CAIP10
semantics.
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<String> = /*List of accounts on chains*/
val methods: List<String> = /*List of methods that wallet approves*/
val events: List<String> = /*List of events that wallet approves*/
val namespaces: Map<String, Wallet.Model.Namespaces.Session> = mapOf(namespace, Wallet.Model.Namespaces.Session(accounts, methods, events))
val updateParams = Wallet.Params.SessionUpdate(sessionTopic, namespaces)
Web3Wallet.updateSession(updateParams) { error -> /*callback for error while sending session update*/ }
To update a session with namespaces, submit a Wallet.Params.SessionUpdate
object with the session's topic and namespaces to update session with
to Web3Wallet.updateSession
.
The session_update
event is emitted from the wallet when the session is updated by calling updateSession
. To update a session, pass in the topic and the new namespace.
await web3wallet.updateSession({ topic, namespaces: newNs })
Update a session, adding/removing additional namespaces in the given topic.
var newNamespaces = new Namespaces(...);
var request = await walletClient.UpdateSession(sessionTopic, newNamespaces);
await request.Acknowledged();
Extending a Session
- Web
- iOS
- Android
- Flutter
- React Native
- C#
Sessions have a default expiry of 7 days. To extend a session by an additional 7 days, call .extendSession
method and pass in the topic
of the session you wish to extend.
await web3wallet.extendSession({ topic })
🛠️ Usage examples
⚠️ Expected Errors
Error: No matching key. session topic doesn't exist: 'xyz...'
This rejection means the SDK can't find a session with the given topic
- in this example xyz...
.
This can happen when the session you're trying to update has already been disconnected by either the wallet or the dapp or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic of an active session.
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:
try await Web3Wallet.instance.extend(topic: session.topic)
Above method will extend a user's session to a week.
val sessionTopic: String = /*Topic of Session*/
val extendParams = Wallet.Params.SessionExtend(sessionTopic = sessionTopic)
Web3Wallet.extendSession(extendParams) { error -> /*callback for error while extending a session*/ }
To extend a session, create a Wallet.Params.SessionExtend
object with the session's topic to update the session with to Web3Wallet.extendSession
. Session is
extended by 7 days.
To extend the session, call the extendSession
method and pass in the new topic
. The session_update
event will be emitted from the wallet.
await web3wallet.extendSession({ topic })
Extend a session's expiry time so the session remains open
var request = await walletClient.Extend(sessionTopic);
await request.Acknowledged();
Session Disconnect
- Web
- iOS
- Android
- Flutter
- React Native
- C#
To initiate disconnnect from a session(think session delete), call .disconnectSession
by passing a topic
& reason
for the disconnect.
The other peer will receive a session_delete
and be notified that the session has been disconnected.
It's important that you're subscribed to the session_delete
event as well, to be notified when the other peer initiates a disconnect.
We recommend using the getSDKError
utility function, that will provide ready-to-use reason
payloads and is available in the @walletconnect/utils
library.
await web3wallet.disconnectSession({
topic,
reason: getSdkError('USER_DISCONNECTED')
})
🛠️ Usage examples
⚠️ Expected Errors
Error: No matching key. session topic doesn't exist: 'xyz...'
This rejection means the SDK can't find a session with the given topic
- in this example xyz...
.
This can happen when the session you're trying to update has already been disconnected by either the wallet or the dapp or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic of an active session.
For good user experience your wallet should allow users to disconnect unwanted sessions. In order to terminate a session use disconnect
method.
try await Web3Wallet.instance.disconnect(topic: session.topic)
val disconnectionReason: String = /*The reason for disconnecting the Session*/
val disconnectionCode: String = /*The code for for disconnecting the Session*/
val sessionTopic: String = /*Topic from the Session*/
For reference use CAIP-25: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md
val disconnectParams = Wallet.Params.SessionDisconnect(sessionTopic, disconnectionReason, disconnectionCode)
Web3Wallet.disconnectSession(disconnectParams) { error -> /*callback for error while disconnecting a session*/ }
To disconnect from un active session, pass a disconnection reason with code and the Session topic to the Web3Wallet.disconnectSession
function.
When either the dapp or the wallet disconnects from a session, a session_delete
event will be emitted. It's important to subscribe to this event so you could keep your state up-to-date.
To initiate a session disconnect, call the disconnectSession
method and pass in the topic
and reason
. You can use the getSDKError
utility function, which is available in the @walletconnect/utils
library.
await web3wallet.disconnectSession({
topic,
reason: getSdkError('USER_DISCONNECTED')
})
Disconnecting
To disconnect a session, use the Disconnect
function. You may optional provide a reason for the disconnect.
Disconnecting requires the topic
of the session to be given. This can be found in the SessionStruct
object given when a session has been given approval by the Wallet.
var sessionTopic = sessionData.Topic;
await walletClient.Disconnect(sessionTopic);
// or
await walletClient.Disconnect(sessionTopic, Error.FromErrorType(ErrorType.USER_DISCONNECTED));
Auth Requests
Auth requests are used to authenticate a user with a dapp. The dapp will send a request to the wallet, which will then prompt the user to sign a message. The signed message will be sent back to the dapp, which will verify the signature and authenticate the user.
Auth requests are only supported on iOS, Android, Flutter & C#
Setup Auth Requests Handling
- iOS
- Android
- Flutter
- C#
When your Auth
instance receives requests or responses from a peer client, it will publish a related event. So you should set a subscription to handle them.
Web3Wallet.instance.authRequestPublisher
.receive(on: DispatchQueue.main)
.sink { [self self] auth in
self?.verifyDapp(auth.context)
self?.showAuthRequest(auth.request)
}.store(in: &publishers)
VerifyContext
provides a domain verification information about AuthRequest
. 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.
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
}
To approve authorization request, sign message using CacaoSigner.sign
which requires private key to sign Cacao
object that needs to be passed to Wallet.Params.AuthRequestResponse
object and send to Dapp.
issuer
parameter describes what did responder authorizes. Example iss
for Ethereum Mainnet: did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a
. More about did:pkh
method here.
val request: Wallet.Event.AuthRequest = // Request from onAuthRequest
val signature: Wallet.Model.Cacao.Signature = CacaoSigner.sign(
request.message, // Message to be signed
PRIVATE_KEY, // Private key used to signing a message
SignatureType.EIP191 // or EIP1271
)
val issuer = //Check following link for more reference: https://github.com/w3c-ccg/did-pkh/blob/main/did-pkh-method-draft.md
Web3Wallet.respondSessionRequest(Wallet.Params.AuthRequestResponse(request.id, signature, issuer)) { error ->
Log.e("Wallet respond approve", error.throwable.stackTraceToString())
}
Subscribe to the AuthRequested
event handler to listen for auth requests. This event handler
triggers whenever a new auth request is sent to the wallet. The event is given the following object
public class AuthRequest : TopicMessage
{
[JsonProperty("params")]
public AuthRequestData Parameters;
[JsonProperty("verifyContext")]
public VerifiedContext VerifyContext { get; set; }
}
Where AuthRequestData
provides the parameters for the new auth request.
VerifiedContext
provides a domain verification information about AuthRequest
. 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.
Example AuthRequested
event usage:
sdk.AuthRequested += async (sender, authRequest) =>
{
// code here
};
Approve Auth Requests
- iOS
- Android
- Flutter
- C#
Authorization request will be published by authRequestPublisher
. When a wallet receives a request, you want to present it to the user and request a signature. After the user signs the authentication message, the wallet should respond to a dapp.
Type
parameter represent signature validation method which will be used on dapp side. Supported signature validation methods: EIP191, EIP1271. In both cases message will be signed with EIP191 standard.
let signer = MessageSignerFactory.create()
let signature = try signer.sign(message: request.message, privateKey: privateKey, type: .eip191)
try await Web3WalletClient.respond(requestId: request.id, signature: signature, from: account)
You can approve a dapp's auth request by responding with the user's signature.
String sig = 'your sig here';
await web3Wallet.respondAuthRequest(
id: args.id,
iss: 'did:pkh:eip155:1:0x06C6A22feB5f8CcEDA0db0D593e6F26A3611d5fa',
signature: CacaoSignature(t: CacaoSignature.EIP191, s: sig),
);
You can approve a dapp's auth request by responding with the user's signature.
// TEST: Create NEthereum wallet to simulate signing
var wallet = new Wallet(Wordlist.English, WordCount.Twelve);
var walletAddress = wallet.GetAddresses(1)[0];
var iss = $"did:pkh:eip155:1:{walletAddress}";
sdk.AuthRequested += async (sender, authRequest) =>
{
var message = sdk.FormatMessage(authRequest.Parameters.CacaoPayload, iss);
var signature = await wallet
.GetAccount(walletAddress)
.AccountSigningService
.PersonalSign
.SendRequestAsync(
Encoding.UTF8.GetBytes(message)
);
await sdk.RespondAuthRequest(authRequest, signature, iss);
};
Reject Auth Requests
- iOS
- Android
- Flutter
- C#
In case user rejects an authentication request, call:
try await Web3WalletClient.reject(requestId: request.id)
To reject the request, pass in an error code and reason. They can be found here.
await web3Wallet.respondAuthRequest(
id: args.id,
iss: 'did:pkh:eip155:1:0x06C6A22feB5f8CcEDA0db0D593e6F26A3611d5fa',
error: Errors.getSdkError(Errors.USER_REJECTED_AUTH),
);
To reject the request, pass in an error code and reason. They can be found here.
// TEST: Create NEthereum wallet to simulate signing
var wallet = new Wallet(Wordlist.English, WordCount.Twelve);
var walletAddress = wallet.GetAddresses(1)[0];
var iss = $"did:pkh:eip155:1:{walletAddress}";
sdk.AuthRequested += (sender, authRequest) =>
{
var errorResponse = new Error()
{
Code = 14001,
Message = "Can not login"
};
sdk.RespondAuthRequest(authRequest, errorResponse, iss);
};
Extra (Platform Specific)
- Web
- iOS
- Android
- Flutter
- React Native
Emitting Session Events
To emit session events, call the emitSessionEvent
and pass in the params. If you wish to switch to chain/account that is not approved (missing from session.namespaces
) you will have to update the session first. In the following example, the wallet will emit session_event
that will instruct the dapp to switch the active accounts.
await web3wallet.emitSessionEvent({
topic,
event: {
name: 'accountsChanged',
data: ['0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb']
},
chainId: 'eip155:1'
})
In the following example, the wallet will emit session_event
when the wallet switches chains.
await web3wallet.emitSessionEvent({
topic,
event: {
name: 'chainChanged',
data: 1
},
chainId: 'eip155:1'
})
Subscribe for Web3Wallet Publishers
The following publishers are available to subscribe:
public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never>
public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never>
public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never>
public var sessionPublisher: AnyPublisher<[Session], Never>
public var socketConnectionStatusPublisher: AnyPublisher<SocketConnectionStatus, Never>
public var sessionSettlePublisher: AnyPublisher<Session, Never>
public var sessionDeletePublisher: AnyPublisher<(String, Reason), Never>
public var sessionResponsePublisher: AnyPublisher<Response, Never>
Web3Wallet.WalletDelegate
val walletDelegate = object : Web3Wallet.WalletDelegate {
override fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal, verifyContext: Wallet.Model.VerifyContext) {
// Triggered when wallet receives the session proposal sent by a Dapp
}
override fun onSessionRequest(sessionRequest: Wallet.Model.SessionRequest, verifyContext: Wallet.Model.VerifyContext) {
// Triggered when a Dapp sends SessionRequest to sign a transaction or a message
}
override fun onAuthRequest(authRequest: Wallet.Model.AuthRequest, verifyContext: Wallet.Model.VerifyContext) {
// Triggered when Dapp / Requester makes an authorization request
}
override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) {
// Triggered when the session is deleted by the peer
}
override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) {
// Triggered when wallet receives the session settlement response from Dapp
}
override fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) {
// Triggered when wallet receives the session update response from Dapp
}
override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) {
//Triggered whenever the connection state is changed
}
override fun onError(error: Wallet.Model.Error) {
// Triggered whenever there is an issue inside the SDK
}
}
Web3Wallet.setWalletDelegate(walletDelegate)
Wallet.Event.VerifyContext
provides a domain verification information about SessionProposal, SessionRequest and AuthRequest. It consists of origin of a Dapp from where the request has been sent, validation Enum that says whether origin is VALID, INVALID or UNKNOWN and verify url server.
data class VerifyContext(
val id: Long,
val origin: String,
val validation: Model.Validation,
val verifyUrl: String
)
enum class Validation {
VALID, INVALID, UNKNOWN
}
The Web3Wallet needs a Web3Wallet.WalletDelegate
passed to it for it to be able to expose asynchronous updates sent from the Dapp.
Format message
To receive formatted SIWE message, call formatMessage method with following parameters:
val payloadParams: Wallet.Params.PayloadParams = //PayloadParams received in the onAuthRequest callback
val issuer = //MUST be the same as send with the respond methods and follows: https://github.com/w3c-ccg/did-pkh/blob/main/did-pkh-method-draft.md
val formatMessage = Wallet.Params.FormatMessage(event.payloadParams, issuer)
Web3Wallet.formatMessage(formatMessage)
Register Device Token
This method enables wallets to receive push notifications from WalletConnect's Echo Server via Firebase Cloud Messaging. This means you will have to setup your project with Firebase before being able to call registerDeviceToken method.
Make sure that a service extending the FirebaseMessagingService is added to your manifest as per the Firebase FCM documentation as well as any other setup Firebase requires Firebase setup documentation.
To register a wallet to receive WalletConnect push notifications, call Web3Wallet.registerDeviceToken
and pass the Firebase Access Token.
val firebaseAccessToken: String = //FCM access token received through the Firebase Messaging SDK
Web3Wallet.registerDeviceToken(
firebaseAccessToken,
onSuccess = {
// callback triggered once registered successfully with the Echo Server
},
onError = { error: Wallet.Model.Error ->
// callback triggered if there's an exception thrown during the registration process
})
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 Generatorsflutter 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 moredart format lib/* -l 120
flutter analyze
Emitting Session Events
To emit session events, call the emitSessionEvent
and pass in the params. If you wish to switch to chain/account that is not approved (missing from session.namespaces
) you will have to update the session first. In the following example, the wallet will emit session_event
that will instruct the dapp to switch the active accounts.
await web3wallet.emitSessionEvent({
topic,
event: {
name: 'accountsChanged',
data: ['0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb']
},
chainId: 'eip155:1'
})
In the following example, the wallet will emit session_event
when the wallet switches chains.
await web3wallet.emitSessionEvent({
topic,
event: {
name: 'chainChanged',
data: ['0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb']
},
chainId: 'eip155:1'
})
Was this helpful?