open-node-ui
v1.0.4
Published

Readme
OPEN NODE UI

OPEN NODE UI is a lightweight React component library and SDK built to simplify the development of decentralized applications (dApps) on Ethereum and EVM-compatible chains.
It provides ready-to-use UI components and essential utilities tailored for smart contract interactions.
🚀 Built with React • Vite • TypeScript
🛠️ Focused on developer experience and Web3 integration
💬 ChatBox Component
<ChatBox /> is a fully customizable, responsive AI/Web3 chat interface for integrating smart contract interactions, AI assistants, or support bots directly into your dApp.
It is designed to deliver seamless conversation UX for Ethereum & EVM environments.
🧩 Features
- 📱 Responsive layout (mobile-first with desktop support)
- 🪄 Animated message history using
framer-motion - 🔗 System prompts + persistent conversations
- 🧠 AI-ready input/output management
- 📚 Options (chips) for quick-start messages
- 🎨 Customizable styles: messages, borders, app bar, and more
⚙️ Props Overview
| Prop | Type | Description |
|--------------------|----------------------------------------|-------------|
| isChatOpen | boolean | Controls visibility of the chat |
| setIsChatOpen | (open: boolean) => void | Toggles chat UI |
| inputText | string | Current input message |
| setInputText | (text: string) => void | Updates the input field |
| onChat | (messages: MessageType[]) => Promise<MessageType> | Handles AI/smart contract response |
| onAddMessageToDb | (msg, convoId) => Promise<Conversation> | Persists message to backend |
| options | OptionItem[] | Quick-reply chips |
| logo | ReactNode | Optional branding logo |
| title | string | Chat window title |
| systemPrompt | string | Initial system instruction |
| chatWidgetStyle | React.CSSProperties (optional) | Style override for the widget |
| appBarStyle | React.CSSProperties (optional) | Style override for the app bar |
| aiMsgStyle | React.CSSProperties (optional) | Style for AI/assistant messages |
| userMsgStyle | React.CSSProperties (optional) | Style for user messages |
| bordersColor | string (optional) | Color for message box separators |
🚀 Example Usage
import { ChatBox } from "open-node-ui";
<ChatBox
isChatOpen={isChatOpen}
setIsChatOpen={setIsChatOpen}
buttonSendRef={sendButtonRef}
textInputRef={textInputRef}
inputText={inputText}
setInputText={setInputText}
onChat={handleOnChat}
onAddMessageToDb={handleSaveMessage}
options={[
{ title: "What is staking?", icon: <SomeIcon /> },
{ title: "How do I connect a wallet?" }
]}
systemPrompt="You are a helpful Web3 assistant."
title="Web3 Assistant"
bordersColor="#e5e5e5"
/>
🧱 AppBar Component
<AppBar /> is a flexible top navigation bar designed for dApps and modular interfaces.
It serves as the header for panels, modals, or standalone views — supporting actions, custom titles, and back/close interactions.
✨ Features
- 🧭 Customizable layout with optional title, leading icon (e.g. back button), and action buttons
- 📦 Fixed or relative positioning to adapt to various container types
- 🎯 Built-in close/back logic via the
onClosecallback - 💅 Easy to theme with
classNameand styling props
⚙️ Props Overview
| Prop | Type | Description |
|-------------|-------------------|-------------|
| title | React.ReactNode | Title text or custom JSX element |
| leading | React.ReactNode | Optional leading element (e.g. logo or back icon) |
| actions | React.ReactNode[] | Action buttons/icons (right side) |
| fixed | boolean | Whether to fix the AppBar to the top (true by default) |
| zIndex | number | Sets z-index for layering |
| className | string | Custom class name for styling |
| onClose | () => void | Callback for closing or navigating back |
🚀 Example Usage
import { AppBar } from "open-node-ui";
import { X } from "lucide-react";
<AppBar
title="Transaction Panel"
leading={<img src="/logo.svg" alt="logo" />}
actions={[<X onClick={() => console.log("Close")} />]}
onClose={() => console.log("Back pressed")}
/>🪟 Dialog Component
<Dialog /> is a responsive modal sheet built for displaying focused content such as forms, transaction previews, wallet interactions, and confirmations.
It includes a smooth open/close animation and works well on both desktop and mobile.
✨ Features
- 📱 Mobile-friendly modal layout with animated scale transitions
- 🧩 Optional header and content sectioning
- 💅 Highly customizable styles for container and overlay
- 🔐 Controlled visibility using the
openprop
⚙️ Props Overview
| Prop | Type | Description |
|--------------------------|-----------------------|-------------|
| open | boolean | Controls visibility of the modal |
| children | React.ReactNode | Content rendered inside the modal |
| header | React.ReactNode | Optional header content (e.g. title, icon) |
| containerStyle | React.CSSProperties | Inline styles for the modal container |
| className | string | Additional classes for the modal container |
| ContentTopSpace | string | Margin above the content section (default "0px") |
| barrierStyle (unused)| React.CSSProperties | (Reserved for future use — modal background barrier) |
| positionedContainerStyle | React.CSSProperties | Styles for the full-screen wrapper (positioning, z-index, etc.) |
🚀 Example Usage
import { Dialog } from "open-node-ui";
<Dialog
open={isOpen}
header={<h2>Confirm Transaction</h2>}
containerStyle={{ backgroundColor: "#fff", borderRadius: "16px" }}
ContentTopSpace="20px"
>
<p>Are you sure you want to proceed with this transaction?</p>
<button onClick={handleConfirm}>Confirm</button>
</Dialog>📝 ListTitle Component
<ListTitle /> is a flexible header-like component used to label sections in lists, modals, or navigation panels.
It combines a title, optional subtitle, and interactive icons (like back buttons or actions), making it ideal for top-level list headers and contextual panels.
✨ Features
- 🧭 Optional back icon or leading content
- 📄 Supports subtitles and section descriptions
- 🧩 Highly customizable styles for title, subtitle, and container
- 🔘 Clickable container for navigation or action triggers
- 🎯 Action icons aligned to the right
⚙️ Props Overview
| Prop | Type | Description |
|--------------------|-----------------------|-------------|
| title | React.ReactNode | Primary title text |
| subTitle | React.ReactNode | Optional subtitle below the title |
| leading | React.ReactNode | Left-side content (e.g. back icon) |
| actions | React.ReactNode[] | Right-side icons/buttons |
| onClick | () => void | Callback when the header is clicked |
| className | string | Custom class for outer container |
| listTitleStyle | React.CSSProperties | Style for the outer container |
| leadingStyle | React.CSSProperties | Style for the leading icon or element |
| titleStyle | React.CSSProperties | Style for the title text |
| subtitleStyle | React.CSSProperties | Style for the subtitle text |
| actionsStyle | React.CSSProperties | Style for the right-side action container |
| mainTextStyle | React.CSSProperties | Style wrapper for both title and subtitle |
| leftStyle | React.CSSProperties | Style wrapper for the left side (icon + title) |
🚀 Example Usage
import { ListTitle } from "open-node-ui";
import { X } from "lucide-react";
<ListTitle
title="Settings"
subTitle="Manage your preferences"
onClick={() => console.log("Go back")}
actions={[<X onClick={() => console.log("Close")} />]}
titleStyle={{ fontWeight: "bold" }}
/>📥 ModalBottomSheet Component
<ModalBottomSheet /> is a sleek, animated modal that slides up from the bottom of the screen — ideal for mobile dialogs, confirmations, or dynamic content in decentralized apps.
It's designed to be minimal, flexible, and smooth in transitions.
✨ Features
- 📱 Mobile-first design with bottom-up appearance
- 🎞️ Animated visibility transitions using
ElementOpenandElementClose - 📦 Customizable content and header area
- 🧩 Flexible container styling for height, width, padding, etc.
⚙️ Props Overview
| Prop | Type | Description |
|-------------------|-----------------------|-------------|
| open | boolean | Controls visibility of the modal |
| children | React.ReactNode | Main content of the modal |
| header | React.ReactNode | Optional top section (e.g. title, icon) |
| containerStyle | React.CSSProperties | Inline styles for the outer container |
| className | string | Additional class names for styling |
| ContentTopSpace | string | Space above the content body (default: "0px") |
| barrierStyle (unused) | React.CSSProperties | Reserved for background overlay (future use) |
🚀 Example Usage
import { ModalBottomSheet } from "open-node-ui";
<ModalBottomSheet
open={isModalOpen}
header={<h3>Wallet Connected</h3>}
containerStyle={{ backgroundColor: "#fff", borderTopLeftRadius: 20, borderTopRightRadius: 20 }}
ContentTopSpace="15px"
>
<p>Your wallet is now connected. You may proceed with the transaction.</p>
</ModalBottomSheet>🛡️ BarrierOverlay Component
<BarrierOverlay /> is a backdrop layer used behind modals and bottom sheets to dim the background and prevent interaction with the underlying UI.
It also handles dismissal actions when users tap outside a modal area.
✨ Features
- 🕶️ Dynamic opacity and z-index control for smooth entrance/exit
- 🔄 Click-to-close functionality via
toggleModalStatecallback - 🎛️ Configurable animation properties for both open and close states
⚙️ Props Overview
| Prop | Type | Description |
|------------------|-------------|-------------|
| isOpen | boolean | Controls the visibility and layering of the overlay |
| toggleModalState | () => void | Callback triggered on background click |
| openZIndex | number | Z-index when overlay is active (default: 1000) |
| closeZIndex | number | Z-index when overlay is hidden (default: -1) |
| openOpacity | number | Opacity when overlay is active (default: 1) |
| closeOpacity | number | Opacity when overlay is hidden (default: 0) |
🚀 Example Usage
import { BarrierOverlay } from "open-node-ui";
<BarrierOverlay
isOpen={isModalVisible}
toggleModalState={() => setIsModalVisible(false)}
openOpacity={0.6}
openZIndex={1300}
/>🔄 CircularLoader & FixedNormalLoader Components
These two components offer loading spinners for various use cases in your dApp UI — from fullscreen blocking loaders to inline activity indicators.
🎡 CircularLoader
A fullscreen, center-aligned loader with optional blur, text, and customizable styles.
It’s commonly used to indicate blocking states such as wallet connection, transaction confirmation, or page transitions.
✨ Features
- 🌀 Multiple built-in animation types
- 🌫️ Optional blurred background
- 🧾 Optional loading message
- 🎨 Style overrides via
loaderStyleobject
⚙️ loaderStyle Props
| Prop | Type | Description |
|------------------------|-----------------------|-------------|
| type | 0 | 1 | 2 | 3 | Loader animation style (default: 0) |
| barrierStyle | React.CSSProperties | Background container style |
| loaderContainerStyle | React.CSSProperties | Loader inner wrapper styles |
| loaderStyle | React.CSSProperties | Actual loader element styling |
| text | string | Optional loading text |
| textStyle | React.CSSProperties | Style for the loading text |
| useBlur | boolean | Enables backdrop blur if true |
🚀 Example Usage
import { CircularLoader } from "open-node-ui";
<CircularLoader
styles={{
type: 1,
text: "Connecting to wallet...",
useBlur: true,
textStyle: { color: "#fff", fontSize: "14px" },
}}
/>🔔 Toast Notification System
This module provides a React context-based toast notification system allowing easy global toast messages management throughout your app.
🧩 ToastProvider
A React context provider that manages a list of toast messages and handles their lifecycle including showing, auto-dismissing, and manual deletion.
Features
- Generates unique IDs for each toast using
uuid - Supports automatic removal of toast messages after a customizable duration (default 5000ms)
- Gracefully handles exit animations by toggling
isExitingflag before removing from state - Allows disabling auto-delete for persistent toasts
- Passes
showToastfunction via context for easy access in components - Renders all active toast messages via the internal
Toastcomponent
Props
| Prop | Type | Description |
| -------- | ------------- | ----------------------------- |
| children | ReactNode | Child components wrapped by this provider |
Usage Example
Wrap your app or part of the component tree where you want toast notifications:
import { ToastProvider } from "open-node-ui";
<ToastProvider>
<YourApp />
</ToastProvider>Use case
import { useToast } from "open-node-ui";
const MyComponent = () => {
const { showToast } = useToast();
const notify = () => {
showToast({
message: "This is a success notification!",
duration: 3000,
autoDelete: true,
addDeleteAction: true,
type: "success",
});
};
return <button onClick={notify}>Notify</button>;
};
Toast Message type
type ToastMessage = {
id?: string;
message: React.ReactNode | string;
type?: "success" | "error" | "info" | "warning";
duration?: number; // in milliseconds
autoDelete?: boolean; // defaults to true
addDeleteAction?: boolean; // whether to show a manual close action
isExiting?: boolean; // internal use for exit animation
};WEB3 SDK FUNCTIONALIIES
This library also allows you to manage several aspects of connection management between an ethereum wallet (EVMS) and your application (Dapp).
Web3Provider Context
The Web3Provider component and its hook useWeb3 provide a React context for easy integration with Ethereum wallets and networks. It manages wallet connections, network switching, and ETH balance fetching for Ethereum and EVM-compatible blockchains.
Features
- Connect to Ethereum wallets via the browser's injected
ethereumprovider (e.g., MetaMask). - Automatically switch networks if the connected wallet is on a different chain than specified.
- Access the currently connected account and list of accounts.
- Retrieve ETH balance of the connected account.
- Disconnect and reset connection state.
- Access the underlying
web3instance for advanced contract interactions.
Usage
Wrap your app or component tree with the Web3Provider component and pass the desired rpcUrl and chainId:
import { Web3Provider } from "open-node-ui";
function App() {
return (
<Web3Provider rpcUrl="https://mainnet.infura.io/v3/YOUR_INFURA_KEY" chainId={1}>
<YourComponent />
</Web3Provider>
);
}
// in walletInfo.tsx
import { useWeb3 } from "open-node-ui";
function WalletInfo() {
const { account, connect, disconnect, getEthBalance, isConnected } = useWeb3();
return (
<div>
{isConnected ? (
<>
<p>Connected account: {account}</p>
<button onClick={disconnect}>Disconnect</button>
<p>ETH Balance: {getEthBalance()}</p>
</>
) : (
<button onClick={connect}>Connect Wallet</button>
)}
</div>
);
}
Web3Utils Utility Class
The Web3Utils class provides helpful static utility functions related to Ethereum addresses, improving usability and display in your dApp.
Methods
truncatedAddress(address: string): stringTakes a full Ethereum address and returns a shortened, user-friendly version by truncating the middle part of the address.
This makes displaying addresses in the UI cleaner and more readable.Example:
Web3Utils.truncatedAddress("0x1234567890abcdef1234567890abcdef12345678") // Returns: "0x1234...345678"
``
TokenStateLess and TokenStateFull Classes
These classes provide convenient interfaces to interact with ERC-20 token contracts using Web3, enabling read-only and state-changing operations respectively.
TokenStateLess
This class is designed for read-only interactions with a token contract. It requires a web3ContextType instance and the token's contract address during initialization.
Key Methods:
createContract()
Initializes and returns a Web3 contract instance using the token ABI and contract address.totalSupply()
Fetches the total supply of the token from the blockchain and returns it as a decimal number (adjusted for 18 decimals).balanceOf(address: string)
Retrieves the token balance of a specific address, adjusted to decimal form.allowance(owner: string, spender: string)
Checks how many tokens thespenderis allowed to spend on behalf of theowner.throwError(error)
Handles and rethrows errors consistently.
TokenStateFull
Extends TokenStateLess and adds write functionality such as approving token allowances.
Key Method:
Approve({ address, amount })
Uses aTransactionSignerto send an approval transaction that authorizes theaddressto spend a specifiedamountof tokens on the user's behalf.address: The spender's address.amount: Number of tokens to approve (defaults to 100,000 tokens with 18 decimals).
Throws errors if Metamask (or Ethereum provider) is not detected or if the transaction fails.
Usage Example
import { TokenStateFull } from "open-node-ui";
import { useWeb3 } from "open-node-ui/hooks/web3/web3_context";
const { web3, account } = useWeb3();
const tokenContractAddress = "0x...";
const token = new TokenStateFull(web3, tokenContractAddress);
// Read total supply
const supply = await token.totalSupply();
// Approve spender
await token.Approve({ address: "0xSpenderAddress", amount: 1000 * 1e18 });TransactionSigner Class
The TransactionSigner class is designed to simplify the process of signing and sending Ethereum transactions using Web3 and the injected Ethereum provider (such as MetaMask). It encapsulates transaction preparation, gas estimation, and sending logic into one reusable class.
import { type web3ContextType } from "../hooks/web3/web3_context";
import type { ResponseMessage } from "../types/types";
export class TransactionSigner {
public web3: web3ContextType;
constructor(web3: web3ContextType) {
this.web3 = web3;
}
signAndSendTransaction = async ({
from,
to,
value,
data,
}: {
from: string;
to: string;
value: number;
data: string;
}): Promise<ResponseMessage> => {
try {
const ethereum = this.web3.ethereum;
const params = { from, to, value, data };
// Estimate gas needed for the transaction
const estimatedGas = await this.web3.web3!.eth.estimateGas(params);
console.log("estimate fees: ", estimatedGas);
// Round down gas limit
const gasLimit = Math.floor(Number(estimatedGas));
console.log("Estimated gas:", estimatedGas);
console.log("Adjusted gas limit:", gasLimit);
// Request Ethereum provider to send the transaction
const receipt = await ethereum.request({
method: "eth_sendTransaction",
params: [
{
...params,
value: this.web3.web3.utils.toHex(params.value),
gas: this.web3.web3.utils.toHex(gasLimit),
},
],
});
// Return success response with transaction receipt
if (receipt) {
if (typeof receipt === "string") {
return { success: true, message: receipt };
} else {
return { success: true, message: JSON.stringify(receipt) };
}
} else {
throw Error("Failed to Register");
}
} catch (error) {
console.error(error);
return {
success: false,
message: typeof error === "string" ? error : JSON.stringify(error),
};
}
};
}