npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

keyring-chatbot-agent

v1.0.4

Published

A React chat widget SDK with floating button and modal

Downloads

516

Readme

Keyring Chatbot Agent SDK

An AI-powered Web3 chatbot SDK. Provides a floating chat widget with on-chain capabilities: token swaps, sending tokens/NFTs, viewing balances, and natural-language AI conversation.


Features

  • Natural language AI chat — Understands user intent, distinguishes general questions from on-chain tasks
  • Token swaps — Best-price routing via deBridge, automatic ERC-20 approval when needed
  • Send tokens — ERC-20 and native tokens, supports "max", "50%", "$5" amount expressions
  • Send NFTs — ERC-721 and ERC-1155, resolves NFT by name or token ID from the user's wallet
  • View balances — Token list with USD values
  • Trending tokens — Per-chain trending data with quick-buy buttons
  • Wrap / Unwrap — ETH ↔ WETH and equivalents on each chain
  • Approve token — ERC-20 spending allowance
  • Multi-language — English, Japanese, Chinese UI
  • Multi-chain — Ethereum, Optimism, BNB Chain, Polygon, Base, Arbitrum, Avalanche, Linea
  • Full TypeScript — Complete type declarations included

Installation

npm install keyring-chatbot-agent
# or
yarn add keyring-chatbot-agent

Peer dependencies (React 17, 18, or 19):

npm install react react-dom

React Usage

Basic example

import { ChatWidget } from 'keyring-chatbot-agent';
import type { Transaction, TransactionResult } from 'keyring-chatbot-agent';

function App() {
  const handleTransaction = async (
    tx: Transaction
  ): Promise<TransactionResult> => {
    try {
      const hash = await walletClient.sendTransaction(tx);
      return { status: 'success', transactionHash: hash };
    } catch (err: unknown) {
      return { status: 'fail', error: (err as Error).message };
    }
  };

  return (
    <ChatWidget
      account={{ address: '0x...', chainId: 10 }}
      onTransaction={handleTransaction}
    />
  );
}

Full example

import { ChatWidget } from 'keyring-chatbot-agent';
import type { Transaction, TransactionResult } from 'keyring-chatbot-agent';

function App() {
  const handleTransaction = async (
    tx: Transaction
  ): Promise<TransactionResult> => {
    try {
      const hash = await walletClient.sendTransaction(tx);
      return { status: 'success', transactionHash: hash };
    } catch (err: unknown) {
      return { status: 'fail', error: (err as Error).message };
    }
  };

  return (
    <ChatWidget
      account={{ address: userAddress, chainId: 10 }}
      onTransaction={handleTransaction}
      position="bottom-right"
      language="en"
      defaultOpen={false}
      theme={{
        primaryColor: '#5B7FFF',
        buttonSize: 60,
        zIndex: 9999,
      }}
      rpcUrls={{
        1: 'https://mainnet.infura.io/v3/YOUR_KEY',
        10: 'https://optimism-mainnet.infura.io/v3/YOUR_KEY',
        56: 'https://bsc-dataseed.binance.org',
        137: 'https://polygon-rpc.com',
        8453: 'https://mainnet.base.org',
        42161: 'https://arb1.arbitrum.io/rpc',
        43114: 'https://api.avax.network/ext/bc/C/rpc',
        59144: 'https://rpc.linea.build',
      }}
      onOpen={() => console.log('Chat opened')}
      onClose={() => console.log('Chat closed')}
    />
  );
}

With wagmi v2

import { useSendTransaction, useAccount } from 'wagmi';
import { ChatWidget } from 'keyring-chatbot-agent';
import type { Transaction, TransactionResult } from 'keyring-chatbot-agent';

function App() {
  const { address, chainId } = useAccount();
  const { sendTransactionAsync } = useSendTransaction();

  const handleTransaction = async (
    tx: Transaction
  ): Promise<TransactionResult> => {
    try {
      const hash = await sendTransactionAsync({
        to: tx.to as `0x${string}`,
        data: tx.data as `0x${string}`,
        value: BigInt(tx.value || '0'),
      });
      return { status: 'success', transactionHash: hash };
    } catch (err: unknown) {
      return { status: 'fail', error: (err as Error).message };
    }
  };

  return (
    <ChatWidget
      account={address ? { address, chainId: chainId ?? 1 } : undefined}
      onTransaction={handleTransaction}
    />
  );
}

<ChatWidget /> Props

| Prop | Type | Default | Required | Description | | --------------- | ------------------------------------------------- | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | account | Account | undefined | No | Connected wallet. Omit when no wallet is connected. | | onTransaction | (tx: Transaction) => Promise<TransactionResult> | undefined | * | Called when the chatbot needs to sign/send a transaction. | | position | 'bottom-right' | 'bottom-left' | 'bottom-right' | No | Corner where the floating button appears. | | language | 'en' | 'ja' | 'cn' | 'en' | No | UI language. | | theme | ChatWidgetTheme | {} | No | Visual customization (color, size, z-index). | | defaultOpen | boolean | false | No | Open the chat modal on first render. | | rpcUrls | Record<number, string> | — | No | Per-chain RPC URL overrides. Takes priority over built-in defaults for balance queries and gas estimation. | | onOpen | () => void | — | No | Callback fired when the modal opens. | | onClose | () => void | — | No | Callback fired when the modal closes. |

* onTransaction is required to execute on-chain actions (swap, send, approve, etc.). Without it, the AI can still answer questions and display information.


HTML / Web Component Usage

The SDK ships a Web Component build (chat-widget-wc) that can be embedded in any HTML page without a React build pipeline. The Web Component supports the same full feature set as the React component: wallet connection, transaction signing, RPC URL overrides, language selection, and all callbacks.

Configuration via HTML attributes

Simple scalar values (strings, numbers, booleans) can be set directly on the HTML tag:

| Attribute | Type | Default | Description | | --------------- | --------- | ---------------- | ------------------------------------------------ | | position | string | 'bottom-right' | 'bottom-right' or 'bottom-left' | | primary-color | string | '#007bff' | Widget accent color (hex or any CSS color value) | | button-size | number | 60 | Floating button diameter in pixels | | z-index | number | 9999 | CSS z-index of the widget | | default-open | boolean | false | Set to "true" to open the chat on page load | | language | string | 'en' | UI language: 'en', 'ja', or 'cn' |

Configuration via JavaScript properties

Complex objects (account, rpcUrls, callbacks) are assigned as JavaScript properties on the element. Setting any property triggers an immediate re-render.

| Property | Type | Description | | --------------- | ------------------------------------------------- | ------------------------------------- | | account | { address: string; chainId: number } | Connected wallet info | | onTransaction | (tx: Transaction) => Promise<TransactionResult> | Transaction signing / sending handler | | rpcUrls | Record<number, string> | Per-chain RPC URL overrides | | onOpen | () => void | Callback fired when the modal opens | | onClose | () => void | Callback fired when the modal closes |

Via CDN — UMD script tag

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My dApp</title>
  </head>
  <body>
    <!-- 1. Load the bundle -->
    <script src="https://unpkg.com/keyring-chatbot-agent/dist/chat-widget-wc.umd.js"></script>

    <!-- 2. Place the custom element with basic attributes -->
    <chat-widget
      id="my-chat"
      position="bottom-right"
      primary-color="#5B7FFF"
      button-size="60"
      z-index="9999"
      language="en"
    ></chat-widget>

    <!-- 3. Assign account and onTransaction via JavaScript -->
    <script>
      const widget = document.getElementById('my-chat');

      // Set wallet info (reassign whenever the wallet changes)
      widget.account = { address: '0xYourWalletAddress', chainId: 10 };

      // Set the transaction handler
      widget.onTransaction = async function (tx) {
        try {
          // Works with any provider: ethers.js, viem, MetaMask, etc.
          const provider = new ethers.BrowserProvider(window.ethereum);
          const signer = await provider.getSigner();
          const txResponse = await signer.sendTransaction({
            to: tx.to,
            data: tx.data,
            value: BigInt(tx.value || '0'),
          });
          const receipt = await txResponse.wait();
          return { status: 'success', transactionHash: receipt.hash };
        } catch (err) {
          return { status: 'fail', error: err.message };
        }
      };

      // Optional: override RPC URLs
      widget.rpcUrls = {
        10: 'https://optimism-mainnet.infura.io/v3/YOUR_KEY',
        8453: 'https://mainnet.base.org',
      };
    </script>
  </body>
</html>

Via CDN — ES module

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My dApp</title>
  </head>
  <body>
    <chat-widget
      id="my-chat"
      position="bottom-right"
      language="en"
    ></chat-widget>

    <script type="module">
      import 'https://unpkg.com/keyring-chatbot-agent/dist/chat-widget-wc.es.js';

      const widget = document.getElementById('my-chat');

      widget.account = { address: '0xYourWalletAddress', chainId: 10 };

      widget.onTransaction = async (tx) => {
        // handle transaction signing here
      };
    </script>
  </body>
</html>

Full MetaMask integration (plain HTML)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My dApp</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ethers/6.13.0/ethers.umd.min.js"></script>
    <script src="https://unpkg.com/keyring-chatbot-agent/dist/chat-widget-wc.umd.js"></script>
  </head>
  <body>
    <button id="connect-btn">Connect Wallet</button>
    <chat-widget
      id="my-chat"
      position="bottom-right"
      language="en"
    ></chat-widget>

    <script>
      const widget = document.getElementById('my-chat');

      widget.onTransaction = async function (tx) {
        try {
          const provider = new ethers.BrowserProvider(window.ethereum);
          const signer = await provider.getSigner();
          const txResponse = await signer.sendTransaction({
            to: tx.to,
            data: tx.data,
            value: BigInt(tx.value || '0'),
          });
          const receipt = await txResponse.wait();
          return { status: 'success', transactionHash: receipt.hash };
        } catch (err) {
          return { status: 'fail', error: err.message };
        }
      };

      document.getElementById('connect-btn').onclick = async function () {
        if (!window.ethereum) return alert('Please install MetaMask!');
        const provider = new ethers.BrowserProvider(window.ethereum);
        const accounts = await provider.send('eth_requestAccounts', []);
        const network = await provider.getNetwork();
        widget.account = {
          address: accounts[0],
          chainId: Number(network.chainId),
        };
      };

      // React to wallet / chain changes
      if (window.ethereum) {
        window.ethereum.on('accountsChanged', async (accounts) => {
          if (accounts.length === 0) {
            widget.account = undefined;
          } else {
            const provider = new ethers.BrowserProvider(window.ethereum);
            const network = await provider.getNetwork();
            widget.account = {
              address: accounts[0],
              chainId: Number(network.chainId),
            };
          }
        });
        window.ethereum.on('chainChanged', async () => {
          const provider = new ethers.BrowserProvider(window.ethereum);
          const network = await provider.getNetwork();
          const accounts = await provider.listAccounts();
          if (accounts.length > 0) {
            widget.account = {
              address: accounts[0].address,
              chainId: Number(network.chainId),
            };
          }
        });
      }
    </script>
  </body>
</html>

Type Definitions

interface Account {
  address: string; // Wallet address (0x...)
  chainId: number | string; // EIP-155 chain ID
}

interface Transaction {
  from: string;
  to: string;
  data: string; // ABI-encoded calldata (hex)
  value: string; // Native token amount in wei
  gasLimit?: string;
  gasPrice?: string;
  maxFeePerGas?: string;
  maxPriorityFeePerGas?: string;
  nonce?: number;
  chainId?: number | string;
}

interface TransactionResult {
  status: 'success' | 'fail';
  transactionHash?: string; // Present on success
  error?: string; // Present on failure
}

interface ChatWidgetTheme {
  primaryColor?: string; // Hex / CSS color
  buttonSize?: number; // Floating button size in px
  zIndex?: number; // CSS z-index
}

type Language = 'en' | 'ja' | 'cn';

Supported Chains

| Chain | Chain ID | | --------- | -------- | | Ethereum | 1 | | Optimism | 10 | | BNB Chain | 56 | | Polygon | 137 | | Base | 8453 | | Arbitrum | 42161 | | Avalanche | 43114 | | Linea | 59144 |


AI Capabilities

The embedded AI (GPT-4.1-mini via Moralis Cortex) understands natural language and automatically routes to on-chain actions:

| What the user says | Action performed | | ---------------------------------- | ------------------------------------ | | "Swap 1 ETH to USDC" | Token swap via deBridge | | "Swap max USDT to ETH" | Swap with full-balance resolution | | "Buy WBTC" | Trending token list for selection | | "Send 10 USDC to 0x..." | ERC-20 transfer | | "Send 0.05 ETH to 0x..." | Native token transfer | | "Wrap 1 ETH" / "Unwrap 0.5 WETH" | Wrap / unwrap native token | | "Show my NFTs" | Link to NFT gallery | | "Send my Bored Ape #1234 to 0x..." | ERC-721 / ERC-1155 transfer | | "What is the current gas fee?" | General blockchain Q&A | | "What tokens are trending?" | Trending list with quick-buy buttons | | "What is my balance?" | Token list with USD values |

Smart swap flow

The AI handles missing parameters gracefully:

| Available information | Behaviour | | -------------------------------- | --------------------------------------------- | | token_in + token_out + amount | Full auto-swap: estimate → confirm → execute | | token_in + token_out (no amount) | Shows 25 / 50 / 75 / 100 % amount selector | | token_out only | Shows wallet balances to choose source token | | token_in only | Shows trending tokens to choose destination | | Neither token specified | Asks the user to specify both tokens | | ERC-20 needs approval | Prompts Approve step before swap | | Insufficient balance or fee | Warning message, no confirmation button shown |


Build Outputs

| File | Format | Use case | | ---------------------------- | ------ | ------------------------------------------ | | dist/chat-widget.es.js | ESM | React / Vite / bundler | | dist/chat-widget.umd.js | UMD | CommonJS / require | | dist/chat-widget-wc.es.js | ESM | Web Component via <script type="module"> | | dist/chat-widget-wc.umd.js | UMD | Web Component via <script src> / CDN | | dist/lib.d.ts | Types | TypeScript declarations |

Package exports map

{
  ".": {
    "import": "dist/chat-widget.es.js",
    "require": "dist/chat-widget.umd.js",
    "types": "dist/lib.d.ts"
  },
  "./web-component": {
    "import": "dist/chat-widget-wc.es.js",
    "require": "dist/chat-widget-wc.umd.js"
  }
}

Development

# Clone
git clone [email protected]:bacoorteam/keyring-chatbot-agent-sdk.git
cd keyring-chatbot-agent-sdk

# Install dependencies
yarn install

# Start demo dev server
yarn dev

# Build both React and Web Component bundles
yarn build

# Build React bundle only
yarn build:react

# Build Web Component bundle only
yarn build:wc

# Watch mode (auto-rebuild on change)
yarn watch

# Lint
yarn lint
yarn lint:fix

# Format
yarn format

Project Structure

src/
├── components/
│   ├── ActionForm.tsx           # Inline transaction forms (send, approve, ...)
│   ├── ChatButton.tsx           # Floating action button
│   ├── ChatModal.tsx            # Chat UI + all AI and on-chain logic
│   ├── ChatWidget.tsx           # Root component — public API entry point
│   ├── MessageContent.tsx       # Markdown message renderer
│   ├── ScrollToBottomButton.tsx
│   └── WalletUserInfo.tsx
├── constants/
│   ├── agentActions.ts          # AgentActionType definitions + form schemas
│   ├── chains.ts                # Supported chain configs
│   ├── storage.ts               # localStorage key constants
│   └── systemPrompt.ts          # AI system prompt builder
├── contexts/
│   ├── ConfigContext.tsx        # RPC URLs + theme config context
│   ├── ConnectContext.tsx       # Wallet account context
│   └── LanguageContext.tsx      # i18n language context
├── hooks/
│   └── useChatMessages.ts       # Chat message state management
├── services/
│   ├── BaseApi.ts               # Base HTTP client
│   ├── deBridge.ts              # deBridge swap quote + transaction API
│   ├── gemini.ts                # Gemini AI: question classification, general chat
│   ├── moralis.ts               # Moralis AI chat, wallet balances, NFTs, metadata
│   ├── token.ts                 # Token info + trending data
│   └── web3.ts                  # Transaction builder, fee estimation, allowance check
├── types/
│   └── index.ts                 # All public TypeScript interfaces
├── lib.tsx                      # React library entry point
└── web-component.tsx            # Web Component entry point

Publishing

# 1. Bump version in package.json
# 2. Build
yarn build

# 3. Login to npm
npm login

# 4. Publish
npm publish

License

ISC

Repository

Bitbucket — keyring-chatbot-agent-sdk

Issues

Issue tracker