Skip to main content

Integrate Web3Auth into a dApp

Introduction

Web3Auth is a wallet infrastructure that is plugged into dApps or wallets. It serves as a pluggable authentication infrastructure for web3 wallets and applications. With Web3Auth's excellent user experience, both mainstream and crypto natives may be onboarded in a matter of minutes.

As a wallet infrastructure, it provides out-of-the-box support for all social logins, web and mobile native platforms, wallets, and other key management methods. By the end of this guide, you will have integrated Web3Auth into your decentralized web application built on the Kaia Network. To integrate Web3Auth into other platforms (Android, iOS, React Native, Flutter, & Unity), kindly refer to this guide.

For a quick start, the complete code for this tutorial is available on GitHub. You can clone or download the repository to follow along.

Prerequisite

  • A working react project (by executing npm create vite@latest project-name -- --template react-ts)
  • Install the necessary wallets (Coinbase Wallet, Metamask).
  • RPC Endpoint: you can get this from one of the supported endpoint providers.
  • Test KAIA from Faucet: fund your account with sufficient KAIA.
  • Get your Client ID from Web3Auth Dashboard.

Installation

To make use of Web3Auth in your dApp, you must install the required libraries and SDK first. Hence, you'll need to set up ethers.js, and the Web3Auth Web SDK. You can use Web3Auth together with either ethers.js, web3.js or kaia sdk libraries to communicate with the Kaia blockchain. We'll be using ethers.js for this guide.


npm install --save @web3auth/modal @web3auth/base @web3auth/ethereum-provider @web3auth/default-evm-adapter
npm install --save ethers

Initializing Web3Auth and Provider Instance

After successfully installing the needed libraries, next is to initialize the Web3Auth instance, set the Web3Auth provider instance in a useState() hook and also the init() function in a useEffect() hook.

Import Web3Auth and other dependent packages.

Import React hooks (useState and useEffect) and utility functions:

  • useState and useEffect: React hooks for state management and side effects.
  • RPC: Custom utility functions from etherRPC.ts for Ethereum-compatible blockchain interactions using ethers.js.

Paste your Client ID from the Web3Auth Dashboard.

Setup Chain Config: To use Web3Auth, you need to set up a chain config for the selected chain - Kaia.

Initialize Web3Auth by using the constructor, where you can pass all the configurations of Web3Auth you want.

Set the Web3Auth provider instance, userInfo in a useState() hook and the init() function in a useEffect() hook.

App.tsx
etherRPC.ts
package.json
vite.config.ts

import {
CHAIN_NAMESPACES,
IProvider,
IAdapter,
WEB3AUTH_NETWORK,
} from '@web3auth/base'
import { EthereumPrivateKeyProvider } from '@web3auth/ethereum-provider'
import { Web3Auth, Web3AuthOptions } from '@web3auth/modal'
import { getDefaultExternalAdapters } from '@web3auth/default-evm-adapter'
import './App.css'
import { useEffect, useState } from 'react'
import RPC from './etherRPC'
const clientId =
'BPi5PB_UiIZ-cPz1GtV5i1I2iOSOHuimiXBI0e-Oe_u6X3oVAbCiAZOTEBtTXw4tsluTITPqA8zMsfxIKMjiqNQ'
const chainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: '0x3e9', // Kairos Testnet
rpcTarget: 'https://public-en-kairos.node.kaia.io',
displayName: 'Kairos Testnet',
blockExplorerUrl: 'https://kairos.kaiascan.io',
ticker: 'KLAY',
tickerName: 'KLAY',
}
const privateKeyProvider = new EthereumPrivateKeyProvider({
config: { chainConfig },
})
const web3AuthOptions: Web3AuthOptions = {
clientId,
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
privateKeyProvider,
}
const web3auth = new Web3Auth(web3AuthOptions)
const adapters = await getDefaultExternalAdapters({ options: web3AuthOptions })
adapters.forEach((adapter: IAdapter<unknown>) => {
web3auth.configureAdapter(adapter)
})
interface UserInfo {
email: string;
name: string;
profileImage: string;
[key: string]: unknown;
}
function App() {
const [provider, setProvider] = useState<IProvider | null>(null);
const [loggedIn, setLoggedIn] = useState<boolean>(false);
const [loading, setLoading] = useState<boolean>(true);
const [address, setAddress] = useState<string>("");
const [balance, setBalance] = useState<string>("");
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
useEffect(() => {
const init = async () => {
try {
await web3auth.initModal()
setProvider(web3auth.provider)
if (web3auth.connected) {
setLoggedIn(true)
await updateUserInfo()
}
} catch (error) {
console.error(error)
} finally {
setLoading(false)
}
}
init()
}, [])
const updateUserInfo = async () => {
if (web3auth.provider) {
const user = await web3auth.getUserInfo()
// @ts-ignore
setUserInfo(user)
const accounts = await RPC.getAccounts(web3auth.provider)
setAddress(accounts)
const balance = await RPC.getBalance(web3auth.provider)
setBalance(balance)
}
}
}

Connecting Wallet

Inside your App function in your App.tsx file, call the connect() method on the web3Auth instance to initiate the connection of your wallet.


function App() {
const [provider, setProvider] = (useState < IProvider) | (null > null)
const [loggedIn, setLoggedIn] = useState < boolean > false
const login = async () => {
if (!web3auth) {
console.log('web3auth not initialized yet')
return
}
const web3authProvider = await web3auth.connect()
setProvider(web3authProvider)
if (web3auth.connected) {
setLoggedIn(true)
await updateUserInfo()
}
}
return (
<div className="App">
<button onClick={login}>Login</button>
</div>
)
}

Setting up Utils function

In this guide, we will be making use of utils function: truncateAddress(). The truncateAddress() function takes in a valid address and returns a more readable format of the address passed in. The following steps below show how to set up and use the utils function in your project.

Step 1: Create a utils.ts file in the src root folder.

Paste the following code in the newly created utils.ts file:


export const truncateAddress = (address) => {
if (!address) return 'No Account'
const match = address.match(
/^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{4})$/
)
if (!match) return address
return `${match[1]}…${match[2]}`
}

Step 2: Import the function in your App.tsx file.


import { truncateAddress } from './utils'

Getting Account and balance

Having connected your wallet successfully by calling the connect() method on the Web3Auth instance, you can get the user account and its balance by using the provider and signer object.


function App() {
const [provider, setProvider] = useState<IProvider | null>(null);
const [address, setAddress] = useState<string>("");
const [balance, setBalance] = useState<string>("");
const getAccounts = async () => {
if (!provider) {
console.log('provider not initialized yet')
return
}
const address = await RPC.getAccounts(provider)
setAddress(address)
console.log('Address:', address)
}
const getBalance = async () => {
if (!provider) {
console.log('provider not initialized yet')
return
}
const balance = await RPC.getBalance(provider)
setBalance(balance)
console.log('Balance:', balance)
}
return (
<div className="App">
<div>
<button onClick={getAccounts}>Get Account</button>
<h2>
{' '}
Address: <span>
{' '}
{`${truncateAddress(address)}` || 'Not available'}
</span>
</h2>
</div>
<div>
<button onClick={getBalance}>Get Balance</button>
<h2>
Balance: <span> {balance || 'Not available'}</span>
</h2>
</div>
</div>
)
}

Disconnecting Wallet

Disconnecting from the wallet is achieved by using the logout() method on the Web3Auth instance. Also, one good practice is to refresh the state to clear any previously stored connection data.


function App() {
const logout = async () => {
if (!web3auth) {
console.log('web3auth not initialized yet')
return
}
await web3auth.logout()
setProvider(null)
setLoggedIn(false)
setAddress('')
setBalance('')
setUserInfo(null)
console.log('Logged out')
}
return (
<div className="App">
<button onClick={logout}>Logout</button>
</div>
)
}

Getting User Info

A unique feature of Web3Auth is social logins. Once a user login using their social platforms, Web3Auth instance returns some information about the logged in user. Getting the logged in user information is as simple as calling the getUserInfo() method on the Web3Auth instance.


const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
const getUserInfo = async () => {
if (!web3auth) {
console.log('web3auth not initialized yet')
return
}
const user = await web3auth.getUserInfo()
// @ts-ignore
setUserInfo(user)
console.log('User Info:', user)
}
return (
<div className="App">
<button onClick={getUserInfo}>Get User Info</button>
<div>
<h2>User Info:</h2>
<pre>
{userInfo ? JSON.stringify(userInfo, null, 2) : 'Not available'}
</pre>
</div>
</div>
)

Signing Messages

Having initialised the provider and signer object, users can sign an arbitrary string.


// add to the existing useState hook.
const [signedMessage, setSignedMessage] = useState<string>("");
const signMessage = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
const originalMessage = "YOUR_MESSAGE";
const signedMessage = await RPC.signMessage(provider, originalMessage);
setSignedMessage(signedMessage);
console.log("Signed Message:", signedMessage);
};
return (
<div className="App">
<button onClick={signMessage}>Sign Message</button>
{signedMessage && (
<div>
<h2 className="text-wrap text-center text-sm font-semibold">
Signed Message: <span className="font-normal">{signedMessage}</span>
</h2>
</div>
)}
</div>
)

Sending Native Transaction

You can perform native transactions, like sending KAIA from one user to another.


// add to the existing useState hook.
const [txHash, setTxHash] = useState<string>("");
const sendKaiaTx = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
console.log("Sending Transaction...");
const destination = "0x75Bc50a5664657c869Edc0E058d192EeEfD570eb";
const amount = "0.1";
const receipt = await RPC.sendKaiaTx(provider, destination, amount);
setTxHash(receipt.hash);
console.log("Transaction Receipt:", receipt);
};
return (
<div className="App">
<button onClick={sendKaiaTx}>Send Kaia</button>
{txHash && (
<div>
<h2>
Transaction Hash:{' '}
<a
href={`${chainConfig.blockExplorerUrl}/tx/${txHash}`}
target="_blank"
rel="noopener noreferrer"
>
{txHash}
</a>
</h2>
</div>
)}
</div>
)

Working with a Smart Contract

You can interact with a deployed smart contract given its Application Binary Interface(ABI) and contract address. The following steps below show how to set up and use the contract address and ABI in your project.

Step 1: Create a constants.ts file in the src root folder.

Paste the following code in the newly created constants.ts file:


export const contractABI = [
{
"inputs": [
{
"internalType": "uint256",
"name": "_initNum",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "retrieve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "num",
"type": "uint256"
}
],
"name": "store",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
] as const;
export const contractAddress = "0x3b01E4025B428fFad9481a500BAc36396719092C";

Step 2: Import the contractABI and contractAddress in your etherRPC.ts file.


import { contractAddress, contractABI } from "./constants";

1. Writing to a Contract


// add to existing useState hook
const [contractTxHash, setContractTxHash] = useState<string>("");
const setContractValue = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
console.log("setting contract value...");
const value = "100";
const tx = await RPC.setContractValue(provider, value);
setContractTxHash(tx.hash);
console.log("Transaction Receipt:", tx);
}
return (
<div className="App">
<button onClick={setContractValue}>Write to Contract</button>
{contractTxHash && (
<div>
<h2>
Contract Tx Hash: <span>{contractTxHash}</span>
</h2>
</div>
)}
</div>
)

2. Reading from a Contract


// add to existing useState hook
const [contractMessage, setContractMessage] = useState<string>("");
const getContractValue = async () => {
if (!provider) {
console.log("provider not initialized yet");
return;
}
console.log("getting value from contract...");
const message = await RPC.getContractValue(provider);
setContractMessage(message);
console.log("contract message:", message);
}
return (
<button onClick={getContractValue}>Read From Contract</button>
{contractMessage && (
<div>
<h2 className="text-wrap text-center text-sm font-semibold">Read Message: <span className="font-normal">{contractMessage}</span></h2>
</div>
)}
)

TroubleShooting

You can visit the troubleshooting page to explore solutions to common challenges and issues from different bundlers.

Next Step

For more in-depth guides on Web3Auth, please refer to the Web3Auth Docs and Web3Auth Github repository.

Make this page better