Integrate with WalletConnect

How to integrate the Venly Wallet API with WalletConnect and build your own wallet that is compatible with every Dapp

WalletConnect is an open-source protocol designed to provide users with a secure way to link their cryptocurrency wallets to DApp websites. This technology enables seamless, direct peer-to-peer interactions between a user's wallet and a DApp, eliminating the need for intermediaries or browser extensions.

Venly's Wallet API enhances this experience by bridging the gap between WalletConnect and DApp websites. This integration allows for efficient interaction with blockchain assets and operations, ensuring a smooth and secure connection for users engaging with decentralized applications.

📘

This guide is intended for wallet app developers looking to implement WalletConnect.

Getting Started

The quickest way to get started is by cloning the repo for our example wallet app (Nextjs/React) and running it locally.

You can test your integration by using it in conjunction with the WalletConnect demo dapp.

Obtain Project ID

Head over to WalletConnect Cloud to sign in or sign up. Create your project and copy its associated Project ID. We will need this in a later step.

Install Packages

Install the Web3Wallet SDK package.

npm install @walletconnect/web3wallet

Initialization

Create a new instance of Core and initialize it with the Project ID created earlier. Next, create a web3Wallet instance while passing a metadata object containing metadata about your app.

import { Core } from '@walletconnect/core'
import { Web3Wallet } from '@walletconnect/web3wallet'

let core, web3wallet

core = new Core({
  projectId: process.env.PROJECT_ID
})

web3wallet = await Web3Wallet.init({
  core,
  metadata: {
    name: 'Venly Demo',
    description: 'Venly WalletConnect Demo App',
    url: 'https://walletconnect.venly.io',
    icons: ['https://storage.venly.io/brand/icon-gradient-background.png']
  }
})

Pairing

await web3wallet.core.pairing.pair({ uri })

The connection between a dapp and your wallet is established using a pairing URI which can be obtained through one of two ways:

  1. Passed as a URL parameter when your wallet is opened by the Web3Modal ex. ?uri=...
  2. Copy/pasted into your wallet app

You can test the pairing flow with the WalletConnect demo dapp. Use the copy icon on the top-right of the Web3Modal to copy the pairing URI.

Events

Once the pairing has been established, the dapp is then able to send requests to your wallet which can be approved/rejected by the user.

Typically, dapps will follow one of two flows:

  1. A session proposal followed by one or more session requests. This is used when the dapp needs the user to perform specific actions, such as sending a transaction or signing a message.
  2. A single auth request used to authenticate the user. This is done by requesting a signed message.

While this guide only covers essential events, you can find more detailed information about all the different event types on the WalletConnect documentation.

Session Proposal

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.

You will want to display a dialog for the user to select their desired wallets and approve or reject the session.

web3wallet.on('session_proposal', (event) => {
  // Show session proposal dialog
})

Approve Session

To approve the session, call approveSession and pass in the proposal.id and approved namespaces. Your namespaces will depend on the user’s selected wallets and respective chains.

See the example wallet app for details on how to build the namespaces object using the buildApprovedNamespaces helper function.

import { buildApprovedNamespaces } from "@walletconnect/utils"

const { id, params } = event
const supportedNamespaces = {
  // accounts: ...
	// chains: ...
	events: ['chainChanged', 'accountsChanged'],
  methods: ['eth_sendTransaction', 'personal_sign', 'eth_sign', 'eth_signTransaction', 'eth_signTypedData']
}
const approvedNamespaces = buildApprovedNamespaces({
  proposal: params,
  supportedNamespaces,
})

await web3wallet.approveSession({
  id: proposal.id,
  namespaces
})

Reject Session

To reject the session proposal, call the rejectSession method. The getSdkError function comes from @walletconnect/utils.

import { getSdkError} from "@walletconnect/utils"

await web3wallet.rejectSession({
  id: proposal.id,
  reason: getSdkError('USER_REJECTED_METHODS')
})

Disconnect Session

If the dapp decides to disconnect the session, the session_delete event will be emitted. Your app should listen for this event in order to update the UI.

You can use the getActiveSessions function to get an updated list of active session pairings.

web3wallet.on('session_delete', (event) => {
  // Update UI with web3wallet.getActiveSessions()
})

To disconnect a session from your wallet, call the disconnectSession function and pass in the topic and reason.

await web3wallet.disconnectSession({
  topic,
  reason: getSdkError('USER_DISCONNECTED')
})

Session Request

The session_request event is triggered by a dapp when it needs the wallet to perform a specific action, such as signing a message. The event contains a request object which will vary depending on the method requested.

To see a list of possible methods, you can refer to the relevant WalletConnect documentation.

web3wallet.on('session_request', (event) => {
  // Show session request dialog
})

Example: Sign Message

For example if the dapp asks for a signed message by requesting the personal_sign method, you will need to implement the logic for signing the message and then call respondSessionRequest with the resulting signature.

const { topic, params, id } = event
const [message, walletAddress] = params.request.params

// Find specified wallet and use it to sign the message
const wallet = EXAMPLE.findWallet(walletAddress)
const signature = EXAMPLE.signMessage(wallet, message)

const response = { id, jsonrpc: '2.0', result: signature }
await web3wallet.respondSessionRequest({ topic, response })

Refer to the example wallet app for examples of other methods.

Auth Request

The auth_request event is emitted when there is a request for authentication from a dapp. Your wallet should respond to this request by signing the provided message and returning the resulting signature. If a user has multiple wallets, you should allow them to select which one they want to authenticate with.

const { id, params } = event
const iss = `did:pkh:eip155:1:${wallet.address}`
const message = web3wallet.formatMessage(params.cacaoPayload, iss)

const signature = EXAMPLE.signMessage(wallet, message)
  
await web3wallet.respondAuthRequest(
  {
    id,
    signature: {
      s: signature,
      t: 'eip191'
    }
  },
  iss
)

You can also reject the auth_request by providing an error object instead.

const iss = `did:pkh:eip155:1:${wallet.address}`
await web3wallet.respondAuthRequest(
  {
    id: event.id,
    error: getSdkError('USER_REJECTED')
  },
  iss
)

Final Step

The last step to complete your integration is to submit your app for approval on the WalletConnect Cloud Explorer. Make sure to follow the Explorer Guidelines to ensure your app meets all the necessary criteria.