Introduction
XRP ledger is a decentralized blockchain that focus on cross-border payment, it is used to document financial transactions, and it is fast and low-cost effective. The XRP Ledger was created by the Ripple co-founder and CEO Chris Larsen. XRP Lede utilized XRP as its native cryptocurrency.
In this tutorial, we are implementing XRP Ledger with web3 auth which will generate a new account for us. This account can be used to perform transactions. It can be used to send tokens to any address on the XRP ledger. For users to get an XRPL account user need to authorize either their Google discord or other auth associated with the web3auth.
To get started with the project. We are going to be utilizing NextJs, Tailwind CSS and other web3 auth libraries.
*Open your terminal *
Navigate to the directory where you want your code to be
*Create a new Nextjs project *
npx create-next-app@latest my-project --typescript --eslint
You should have something like this on your terminal
After you successfully implement that you navigate to my-project
cd my-project
When you cd my-project, You install tailwind css
npm install -D tailwindcss postcss autoprefixer
After it is successful you run this
npx tailwindcss init -p
You need to install some dependencies which we are going to utilize for our project
@web3auth/xrpl-provider - Web3Auth XRPL Provider can be used to interact with wallet or connected EVM compatible chain using RPC calls. refrence web3auth/xrpl-provider
@web3auth/openlogin-adapter - This adapter is a wrapper around the openlogin library from Web3Auth and enables the main social login features of Web3Auth. refrence web3auth/openlogin-adapter
@web3auth/modal - It provides the main class for using the default Web3Auth Modal.
@web3auth/base - Web3Auth Base contains the basic typescript types and interfaces for Web3Auth. This comes in handy by providing you with a standard way of importing the values you need to work with the SDKs. reference web3auth/base
@nice-xrpl/react-xrpl -React-xrpl is a set of hooks and components for creating applications using xrpl.js in React. such as getting wallet balance, sending XRP to another wallet address reference nice-xrpl/react-xrpl
You use yarn as your package installer or any package installer that you know such as npm and others.
yarn add @web3auth/xrpl-provider @web3auth/xrpl-provider @web3auth/openlogin-adapter @web3auth/openlogin-adapter @web3auth/modal @web3auth/base @nice-xrpl/react-xrpl
After the installation, we can open our file directory in our vs code
You can start our development process
In your project directory, you need to create a utils folder and a components folder where we define our provider and other functions that you are going to call such as getting an account that was generated by the web3auth login with the XRP Provider Client.
In the root project directory if you are using src in your project structure you create utils folder if not create it in your root directory, not inside your app or pages folder.
Create a new folder name xrpLRPC.ts
// You start by importing IProvider type from web3auth/base for our typescript
import { IProvider } from "@web3auth/base";
// You import some function which we will use to send input to xrp ledger
import { convertStringToHex, Payment, xrpToDrops } from "xrpl"
// define our class where we will put all our requests
export default class XrplRPC {
// Declaring a private provider variable of type IProvider}
private provider: IProvider;
//You define a Constructor that initializes the provider variable with the provided argument
constructor (provider: IProvider) {
this.provider = provider
}
// Method to get accounts associated with the provider
getAccounts = async (): Promise<any> => {
try {
// Request the accounts using the provider
const accounts = await this.provider.request<never, string[]>({
method: "xrpl_getAccounts", // Specify the method to get accounts
});
console.log(accounts, "accounts"); // Log the accounts for debugging purposes
if (accounts) { // Check if any accounts were returned
// Request account info for the first account in the list
const accInfo = await this.provider.request({
method: "account_info", // Specify the method to get account info
params: [
{
account: accounts[0], // Use the first account
strict: false, // Non-strict mode allows for more lenient account info retrieval
ledger_index: "current", // Use the current ledger index
queue: true, // Include queued transactions
},
],
});
return accInfo; // Return the account info
} else {
return "No account found, please report issues"; // Return an error message if no accounts are found
}
} catch (error) { // Handle any errors that occur
console.error("Error", error); // Log the error
return error; // Return the error
}
};
// Method to get the balance of the first account
getBalance = async (): Promise<any> => {
try {
// Request the accounts using the provider
const accounts = await this.provider.request<string[], never>({
method: "xrpl_getAccounts", // Specify the method to get accounts
});
if (accounts) { // Check if any accounts were returned
// Request account info for the first account in the list
const accInfo = (await this.provider.request({
method: "account_info", // Specify the method to get account info
params: [
{
account: accounts[0], // Use the first account
strict: true, // Strict mode ensures accurate account info
ledger_index: "current", // Use the current ledger index
queue: true, // Include queued transactions
},
],
})) as Record<string, Record<string, string>>;
return accInfo.account_data?.Balance; // Return the account balance
} else {
return "No accounts found, please report this issue."; // Return an error message if no accounts are found
}
} catch (error) { // Handle any errors that occur
console.error("Error", error); // Log the error
return error; // Return the error
}
};
// Method to get the address of the first account
getAccountAddress = async (): Promise<any> => {
try {
// Request the accounts using the provider
const accounts = await this.provider.request<string[], never>({
method: "xrpl_getAccounts", // Specify the method to get accounts
});
if (accounts) { // Check if any accounts were returned
// Request account info for the first account in the list
const accInfo = (await this.provider.request({
method: "account_info", // Specify the method to get account info
params: [
{
account: accounts[0], // Use the first account
strict: true, // Strict mode ensures accurate account info
ledger_index: "current", // Use the current ledger index
queue: true, // Include queued transactions
},
],
})) as Record<string, Record<string, string>>;
return accInfo?.account; // Return the account address
} else {
return "No accounts found, please report this issue."; // Return an error message if no accounts are found
}
} catch (error) { // Handle any errors that occur
console.error("Error", error); // Log the error
return error; // Return the error
}
}
// Method to sign a message
signMessage = async (): Promise<any> => {
try {
const msg = "Hello world this is tutorial on XRPL by Amityclev"; // Define the message to sign
const hexMsg = convertStringToHex(msg); // Convert the message to a hexadecimal string
const txSign = await this.provider.request<{ signature: string }, never>({
method: "xrpl_signMessage", // Specify the method to sign a message
params: {
signature: hexMsg, // Provide the hexadecimal message to be signed
},
});
return txSign; // Return the signed message
} catch (error) { // Handle any errors that occur
console.log("error", error); // Log the error
return error; // Return the error
}
};
// Method to sign and send a transaction
signAndSendTransaction = async (): Promise<any> => {
try {
// Request the accounts using the provider
const accounts = await this.provider.request<never, string[]>({
method: "xrpl_getAccounts", // Specify the method to get accounts
});
if (accounts && accounts.length > 0) { // Check if any accounts were returned and the list is not empty
// Create the payment transaction object
const tx: Payment = {
TransactionType: "Payment", // Specify the transaction type
Account: accounts[0] as string, // Use the first account as the sender
Amount: xrpToDrops(50), // Specify the amount to send, converting XRP to drops
Destination: "rM9uB4xzDadhBTNG17KHmn3DLdenZmJwTy", // Specify the destination address
};
// Request to submit the transaction
const txSign = await this.provider.request({
method: "xrpl_submitTransaction", // Specify the method to submit a transaction
params: {
transaction: tx, // Provide the transaction object
},
});
return txSign; // Return the transaction signature
} else {
return "failed to fetch accounts"; // Return an error message if no accounts are found
}
} catch (error) { // Handle any errors that occur
console.log("error", error); // Log the error
return error; // Return the error
}
};
}
You create a component folder if you have not created already.
Navigate to your component folder
create a file called Web3Authentication.tsx inside compoenent let utilize the various functions we already define in in xrpLRPC.ts
// 'use client' ensures that the following code is executed on the client side
'use client'
// Import necessary modules and components from various packages
import { XrplPrivateKeyProvider } from "@web3auth/xrpl-provider"; // Provides a private key provider for XRPL
import { Web3Auth } from "@web3auth/modal"; // Main Web3Auth module for authentication
import { getXrplChainConfig } from "@web3auth/base"; // Utility function to get XRPL chain configuration
import { CHAIN_NAMESPACES, UX_MODE, WEB3AUTH_NETWORK, IProvider } from "@web3auth/base"; // Various constants and interfaces for Web3Auth
import RPC from "../utils/xrpLRPC"; // Custom RPC class for XRPL interactions
import { OpenloginAdapter } from "@web3auth/openlogin-adapter"; // Adapter for Openlogin
import React, { useEffect, useState } from 'react'; // React hooks for managing state and side effects
// Client ID for Web3Auth which you can get from the web3 auth dashboard https://dashboard.web3auth.io/
const clientId = "BKVW17ohm6Mt-A6O_A633ECD5fOYUEkyOwmU5sdoEhtQFj0PiS28wxLO5DkmbqaAEsCgsd_BzqxhYjabDihcjLc";
// Main component for Web3 authentication
const Web3Authentication = () => {
// Define state variables
const [web3auth, setWeb3auth] = useState<Web3Auth | null>(null); // State for Web3Auth instance
const [provider, setProvider] = useState<IProvider | null>(null); // State for provider instance
const [loggedIn, setLoggedIn] = useState(false); // State to track if the user is logged in
// Configuration for the XRPL chain
const chainConfig = {
chainNamespace: CHAIN_NAMESPACES.XRPL,
chainId: "0x2",
rpcTarget: "https://s.altnet.rippletest.net:51234/", // RPC endpoint for XRPL testnet
wsTarget: "wss://s.altnet.rippletest.net:51233/", // WebSocket endpoint for XRPL testnet
ticker: "XRP",
tickerName: "XRPL",
displayName: "xrpl testnet",
blockExplorerUrl: "https://devnet.xrpl.org/", // Block explorer URL for XRPL testnet
};
// useEffect hook to initialize Web3Auth when the component mounts
useEffect(() => {
const init = async () => {
try {
// Initialize XRPL private key provider
const xrplProvider = new XrplPrivateKeyProvider({
config: {
chainConfig: getXrplChainConfig(0x2), // Get XRPL chain configuration
},
});
console.log(xrplProvider.config, "xrplProvider.config"); // Log provider configuration for debugging
// Initialize Web3Auth with the specified configurations
const web3auth = new Web3Auth({
clientId,
uiConfig: { // UI customization options
appName: "W3A",
theme: {
primary: "red",
},
mode: "dark",
logoLight: "https://web3auth.io/images/web3authlog.png",
logoDark: "https://web3auth.io/images/web3authlogodark.png",
defaultLanguage: "en",
loginGridCol: 3,
primaryButton: "externalLogin",
uxMode: UX_MODE.REDIRECT,
},
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET, // Web3Auth network configuration
privateKeyProvider: xrplProvider, // XRPL private key provider
});
// Initialize and configure Openlogin adapter
const openloginAdapter = new OpenloginAdapter({
loginSettings: {
mfaLevel: "optional",
},
adapterSettings: {
uxMode: "redirect",
whiteLabel: {
logoLight: "https://web3auth.io/images/web3authlog.png",
logoDark: "https://web3auth.io/images/web3authlogodark.png",
defaultLanguage: "en",
},
mfaSettings: {
deviceShareFactor: {
enable: true,
priority: 1,
mandatory: true,
},
backUpShareFactor: {
enable: true,
priority: 2,
mandatory: true,
},
socialBackupFactor: {
enable: true,
priority: 3,
mandatory: true,
},
passwordFactor: {
enable: true,
priority: 4,
mandatory: true,
},
},
},
});
web3auth.configureAdapter(openloginAdapter); // Configure Web3Auth with the Openlogin adapter
setWeb3auth(web3auth); // Set the Web3Auth instance in state
await web3auth.initModal(); // Initialize the Web3Auth modal
if (web3auth.connected) { // Check if already connected
setProvider(web3auth?.provider); // Set the provider instance in state
setLoggedIn(true); // Set logged in state to true
}
} catch (error) {
console.error(error); // Log any errors that occur during initialization
}
};
init(); // Call the init function
}, []);
// Function to handle user login
const login = async () => {
if (!web3auth) {
// If Web3Auth is not initialized, display a message in the UI console
uiConsole("web3auth not initialized yet");
return;
}
// Connect to Web3Auth and set the provider
const webauthProvider = await web3auth.connect();
setProvider(webauthProvider);
setLoggedIn(true); // Update the state to indicate the user is logged in
};
// Function to authenticate the user
const authenticateUser = async () => {
if (!web3auth) {
// If Web3Auth is not initialized, display a message in the UI console
uiConsole("web3auth not initialized yet");
return;
}
// Authenticate the user and get an ID token
const idToken = await web3auth.authenticateUser();
// Display the ID token in the UI console
uiConsole(idToken);
};
// Function to get user information
const getUserInfo = async () => {
if (!web3auth) {
// If Web3Auth is not initialized, display a message in the UI console
uiConsole("web3auth not initialized yet");
return;
}
// Retrieve user information from Web3Auth
const user = await web3auth.getUserInfo();
// Display the user information in the UI console
uiConsole(user);
};
// Function to handle user logout
const logout = async () => {
if (!web3auth) {
// If Web3Auth is not initialized, display a message in the UI console
uiConsole("web3auth not initialized yet");
return;
}
// Log out the user from Web3Auth
await web3auth.logout();
setProvider(null); // Reset the provider state
setLoggedIn(false); // Update the state to indicate the user is logged out
};
// Function to get the accounts associated with the provider
const getAccounts = async () => {
if (!provider) {
// If the provider is not initialized, display a message in the UI console
uiConsole("provider not initialized yet");
return;
}
// Create a new RPC instance with the provider
const rpc = new RPC(provider);
// Get the user accounts from the RPC instance
const userAccount = await rpc.getAccounts();
// Display the account information in the UI console
uiConsole("Account info: ", userAccount);
};
// Function to get the balance of the user account
const getBalance = async () => {
if (!provider) {
// If the provider is not initialized, display a message in the UI console
uiConsole("provider not initialized yet");
return;
}
// Create a new RPC instance with the provider
const rpc = new RPC(provider);
// Get the balance from the RPC instance
const balance = await rpc.getBalance();
// Display the balance in the UI console
uiConsole("Balance", balance);
};
// Function to send a transaction
const sendTransaction = async () => {
if (!provider) {
// If the provider is not initialized, display a message in the UI console
uiConsole("provider not initialized yet");
return;
}
// Create a new RPC instance with the provider
const rpc = new RPC(provider);
// Sign and send the transaction
const result = await rpc.signAndSendTransaction();
// Display the result of the transaction in the UI console
uiConsole(result);
};
// Function to sign a message
const signMessage = async () => {
if (!provider) {
// If the provider is not initialized, display a message in the UI console
uiConsole("provider not initialized yet");
return;
}
// Create a new RPC instance with the provider
const rpc = new RPC(provider);
// Sign the message
const result = await rpc.signMessage();
// Display the signed message in the UI console
uiConsole(result);
};
// Function to get the account address
const getAccountAddress = async () => {
if (!provider) {
// If the provider is not initialized, display a message in the UI console
uiConsole("provider not initialized yet");
return;
}
// Create a new RPC instance with the provider
const rpc = new RPC(provider);
// Get the account address
const result = await rpc.getAccountAddress();
// Display the account address in the UI console
uiConsole(result);
};
// Function to get the wallet seed
// const getWalletSeed = async () => {
// if (!provider) {
// // If the provider is not initialized, display a message in the UI console
// uiConsole("provider not initialized yet");
// return;
// }
// // Create a new RPC instance with the provider
// const rpc = new RPC(provider);
// // Get the wallet seed
// const result = await rpc.getWalletSeed();
// // Log the result in the console for debugging
// console.log(result, "result: ");
// // Display the wallet seed in the UI console
// uiConsole(result);
// };
// Function to display messages in the UI console
function uiConsole(...args) {
// Find the HTML element to display the messages
const el = document.querySelector("#console>p");
if (el) {
// Display the messages in the HTML element as a formatted JSON string
el.innerHTML = JSON.stringify(args || {}, null, 2);
}
}
// Component to display the view when the user is logged in
const loggedInView = (
<>
<div className="flex-container">
<div>
{/* Button to get user information */}
<button onClick={getUserInfo} className="card">
Get User Info
</button>
</div>
<div>
{/* Button to authenticate user and get ID token */}
<button onClick={authenticateUser} className="card">
Get ID Token
</button>
</div>
<div>
{/* Button to get the account address */}
<button onClick={getAccountAddress} className="card">
Address
</button>
</div>
{/* <div>
Button to get the wallet seed
<button onClick={getWalletSeed} className="card">
Seed
</button>
</div> */}
<div>
{/* Button to get user accounts */}
<button onClick={getAccounts} className="card">
Get Accounts
</button>
</div>
<div>
{/* Button to get the balance of the account */}
<button onClick={getBalance} className="card">
Get Balance
</button>
</div>
<div>
{/* Button to sign a message */}
<button onClick={signMessage} className="card">
Sign Message
</button>
</div>
<div>
{/* Button to send a transaction */}
<button onClick={sendTransaction} className="card">
Send Transaction
</button>
</div>
<div>
{/* Button to log out the user */}
<button onClick={logout} className="card">
Log Out
</button>
</div>
</div>
{/* Console to display messages */}
<div id="console" style={{ whiteSpace: "pre-line" }}>
<p style={{ whiteSpace: "pre-line" }}></p>
</div>
</>
);
// Component to display the view when the user is not logged in
const unloggedInView = (
<button onClick={login} className="card">
Login
</button>
);
// Main component to handle the Web3Auth authentication process
return (
<div>
<div className="container">
<h1 className="title">
<a target="_blank" href="https://web3auth.io/docs/sdk/pnp/web/modal" rel="noreferrer">
Web3Auth{" "}
</a>
& ReactJS XRPL Example
</h1>
{/* Display the loggedInView if logged in, otherwise display unloggedInView */}
<div className="grid">{loggedIn ? loggedInView : unloggedInView}</div>
<footer className="footer">
{/* Link to the source code */}
<a
href="https://github.com/Web3Auth/web3auth-pnp-examples/tree/main/web-modal-sdk/blockchain-connection-examples/xrpl-modal-example"
target="_blank"
rel="noopener noreferrer"
>
Source code
</a>
{/* Button to deploy with Vercel */}
<a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FWeb3Auth%2Fweb3auth-pnp-examples%2Ftree%2Fmain%2Fweb-modal-sdk%2Fblockchain-connection-examples%2Fxrpl-modal-example&project-name=w3a-xrpl-modal&repository-name=w3a-xrpl-modal">
<img src="https://vercel.com/button" alt="Deploy with Vercel" />
</a>
</footer>
</div>
</div>
);
}
export default Web3Authentication;
Open your terminal
Make sure you are in the project folder in your terminal
run
yarn run dev
When you login with web3auth in the project, it generate you new a xrpl account, for you.
Noted the account is not activate until you send a token of 15 xrp to the address.
You can check this Demo video on how to send xrp the generated account
Top comments (3)
Hey Muhdsodiq, that's a nice one, but you mentioned a demo video — can you add it please? Would be cool to see it in action.
Also you can add syntax highlighting to your code snipets by adding
typescript
orts
(I believe) after the first three backticks 🎨 (orsh
for terminal command snippets)Thanks for the feedback, sorry I did not understand what you meant by "Also you can add syntax highlighting to your code snipets by adding typescript or ts (I believe) after the first three backticks 🎨 (or sh for terminal command snippets)"
I will provide the link to the video demo working.
Check out this advice — dev.to/hoverbaum/how-to-add-code-h...