Sign In

@thebarmaeffect/barter-react

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thebarmaeffect/barter-react

React hooks and components for the BARTER Proof of Trade Protocol

latest
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

@thebarmaeffect/barter-react

npm version TypeScript License: MIT React

React hooks and context provider for the BARTER Protocol -- a Proof of Trade system. Build trust-aware dApps with idiomatic React patterns: automatic data fetching, loading states, error handling, and refetch support.

Table of Contents

Features

  • Declarative data fetching -- Pass an address or trade ID, get back { data, loading, error, refetch }.
  • Automatic cleanup -- All hooks cancel in-flight requests on unmount or dependency changes. No stale state.
  • Refetch on demand -- Every hook returns a refetch() function for manual re-queries.
  • Context-based configuration -- Configure RPC and network once at the root; all hooks inherit automatically.
  • TypeScript-first -- All hooks, return types, and config interfaces are fully typed.
  • Lightweight -- No runtime dependencies beyond React and ethers. Tree-shakeable ESM build.
  • SSR-safe -- All hooks handle server rendering gracefully with no window access during SSR.

Installation

# npm
npm install @thebarmaeffect/barter-react react ethers

# yarn
yarn add @thebarmaeffect/barter-react react ethers

# pnpm
pnpm add @thebarmaeffect/barter-react react ethers

Peer Dependencies

PackageVersionPurpose
react^18.0.0React framework
ethers^6.0.0Ethereum provider and address validation

Quick Start

Wrap your application in BarterProvider, then use any hook in descendant components.

import { BarterProvider, useBarterScore } from '@thebarmaeffect/barter-react';

function App() {
  return (
    <BarterProvider config={{ rpcUrl: 'https://rpc.sepolia.org', network: 'sepolia' }}>
      <Dashboard />
    </BarterProvider>
  );
}

function Dashboard() {
  const { data: score, loading, error } = useBarterScore('0xAbC123...def456');

  if (loading) return <div>Loading trust score...</div>;
  if (error) return <div>Error: {error.message}</div>;
  return <div>Trust Score: {score}</div>;
}

API Reference

BarterProvider

Context provider that makes BARTER configuration available to all descendant hooks.

import { BarterProvider } from '@thebarmaeffect/barter-react';

<BarterProvider config={config}>
  {children}
</BarterProvider>

Props

PropTypeRequiredDescription
configBarterConfigNoRPC and network configuration
childrenReact.ReactNodeYesChild components that can use BARTER hooks

BarterConfig

interface BarterConfig {
  rpcUrl?: string;   // JSON-RPC endpoint URL (e.g., 'https://rpc.sepolia.org')
  network?: string;  // Network name (e.g., 'sepolia')
}

Example

// Minimal -- uses defaults
<BarterProvider>
  <App />
</BarterProvider>

// With configuration
<BarterProvider config={{
  rpcUrl: 'https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY',
  network: 'sepolia',
}}>
  <App />
</BarterProvider>

// Dynamic configuration
function Root() {
  const [network, setNetwork] = useState('sepolia');

  return (
    <BarterProvider config={{ rpcUrl: RPC_URLS[network], network }}>
      <NetworkSelector value={network} onChange={setNetwork} />
      <App />
    </BarterProvider>
  );
}

useBarterConfig

Access the current BarterConfig from the nearest BarterProvider.

import { useBarterConfig } from '@thebarmaeffect/barter-react';

function NetworkInfo() {
  const config = useBarterConfig();
  return <span>Network: {config.network || 'default'}</span>;
}

Signature

function useBarterConfig(): BarterConfig

Returns: BarterConfig -- The configuration object from the nearest provider, or {} if no provider is found.

useBarterClient

Get the low-level BARTER client instance for direct API access. Returns null if the provider has no configuration.

import { useBarterClient } from '@thebarmaeffect/barter-react';

Signature

function useBarterClient(): BarterClientLike | null

Return Type

interface BarterClientLike {
  trust: unknown;
  trade: unknown;
  credit: unknown;
  compliance: unknown;
  webln: unknown;
}

Returns: BarterClientLike | null -- The client instance, or null if the provider configuration is empty (no rpcUrl and no network).

The client is memoized and only re-created when the provider configuration changes.

Example

function AdvancedQuery() {
  const client = useBarterClient();

  async function handleCustomQuery() {
    if (!client) return;
    // Use client.trust, client.trade, etc. for advanced operations
  }

  return <button onClick={handleCustomQuery}>Run Query</button>;
}

useBarterScore

Fetch the trust score for an Ethereum address. Automatically fetches on mount and when the address changes.

import { useBarterScore } from '@thebarmaeffect/barter-react';

Signature

function useBarterScore(address: string | undefined): UseBarterScoreResult

Parameters

NameTypeDescription
addressstring | undefinedEthereum address to query. Pass undefined to skip fetching.

Return Type

interface UseBarterScoreResult {
  data: number | null;   // The trust score, or null if not yet loaded
  loading: boolean;      // true while the request is in flight
  error: Error | null;   // Error object if the request failed
  refetch: () => void;   // Call to manually trigger a re-fetch
}

Behavior

  • On mount: If address is defined, begins fetching immediately.
  • On address change: Cancels any in-flight request, resets state, and fetches for the new address.
  • On undefined: Resets data to null, loading to false, error to null.
  • On unmount: Cancels any in-flight request. No state updates after unmount.

Example

function TrustScore({ address }: { address: string }) {
  const { data: score, loading, error, refetch } = useBarterScore(address);

  if (loading) return <div className="skeleton" />;
  if (error) return <div className="error">{error.message}</div>;

  return (
    <div>
      <span>Score: {score}</span>
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}

Conditional Fetching

function ConditionalScore() {
  const [address, setAddress] = useState<string | undefined>(undefined);
  const { data: score, loading } = useBarterScore(address);

  return (
    <div>
      <input
        placeholder="Enter address..."
        onChange={(e) => setAddress(e.target.value || undefined)}
      />
      {loading && <span>Loading...</span>}
      {score !== null && <span>Score: {score}</span>}
    </div>
  );
}

useBarterProfile

Fetch the full trust profile for an Ethereum address, including score, trade count, completion rate, counterparty count, and timestamps.

import { useBarterProfile } from '@thebarmaeffect/barter-react';

Signature

function useBarterProfile(address: string | undefined): UseBarterProfileResult

Parameters

NameTypeDescription
addressstring | undefinedEthereum address to query. Pass undefined to skip.

Return Type

interface UseBarterProfileResult {
  data: TrustProfile | null;  // The full profile, or null
  loading: boolean;            // true while fetching
  error: Error | null;         // Error if fetch failed
  refetch: () => void;         // Manual re-fetch trigger
}

TrustProfile

interface TrustProfile {
  address: string;              // The queried address
  score: number;                // Current trust score
  uniqueCounterparties: number; // Number of distinct trade partners
  memberSince: Date | null;     // First trade timestamp, or null
  tradeCount: number;           // Total number of trades
  completionRate: number;       // Fraction of trades settled (0.0 to 1.0)
  lastTradeAt: Date | null;     // Most recent trade timestamp, or null
}

Example

function ProfileCard({ address }: { address: string }) {
  const { data: profile, loading, error, refetch } = useBarterProfile(address);

  if (loading) return <ProfileSkeleton />;
  if (error) return <ErrorBanner message={error.message} />;
  if (!profile) return null;

  return (
    <div className="profile-card">
      <h2>Trust Profile</h2>
      <dl>
        <dt>Score</dt>
        <dd>{profile.score}</dd>

        <dt>Trades</dt>
        <dd>{profile.tradeCount}</dd>

        <dt>Completion Rate</dt>
        <dd>{(profile.completionRate * 100).toFixed(1)}%</dd>

        <dt>Counterparties</dt>
        <dd>{profile.uniqueCounterparties}</dd>

        <dt>Member Since</dt>
        <dd>{profile.memberSince?.toLocaleDateString() ?? 'N/A'}</dd>

        <dt>Last Trade</dt>
        <dd>{profile.lastTradeAt?.toLocaleDateString() ?? 'N/A'}</dd>
      </dl>
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}

useBarterTrade

Fetch the details of a single trade by its ID.

import { useBarterTrade } from '@thebarmaeffect/barter-react';

Signature

function useBarterTrade(tradeId: string | undefined): UseBarterTradeResult

Parameters

NameTypeDescription
tradeIdstring | undefinedTrade ID to query. Pass undefined to skip.

Return Type

interface UseBarterTradeResult {
  data: Trade | null;    // The trade object, or null
  loading: boolean;      // true while fetching
  error: Error | null;   // Error if fetch failed
  refetch: () => void;   // Manual re-fetch trigger
}

Trade

interface Trade {
  tradeId: string;          // Unique identifier
  partyA: string;           // Proposer address
  partyB: string;           // Counterparty address
  paymentHash: string;      // Lightning payment hash (hex)
  amountSats: number;       // Trade amount in satoshis
  status: number;           // Trade status (0=Proposed, 1=Accepted, 2=Settled, 3=Disputed, 4=Expired)
  createdAt: Date;          // Proposal timestamp
  acceptedAt: Date | null;  // Acceptance timestamp, or null
  settledAt: Date | null;   // Settlement timestamp, or null
  category: string;         // Trade category
  description: string;      // Human-readable description
}

Example

function TradeDetail({ tradeId }: { tradeId: string }) {
  const { data: trade, loading, error, refetch } = useBarterTrade(tradeId);

  if (loading) return <Spinner />;
  if (error) return <p>Error loading trade: {error.message}</p>;
  if (!trade) return <p>Trade not found</p>;

  const STATUS_LABELS = ['Proposed', 'Accepted', 'Settled', 'Disputed', 'Expired'];

  return (
    <div>
      <h3>Trade {trade.tradeId}</h3>
      <p>From: {trade.partyA}</p>
      <p>To: {trade.partyB}</p>
      <p>Amount: {trade.amountSats.toLocaleString()} sats</p>
      <p>Status: {STATUS_LABELS[trade.status]}</p>
      <p>Category: {trade.category}</p>
      <p>Created: {trade.createdAt.toLocaleString()}</p>
      {trade.settledAt && <p>Settled: {trade.settledAt.toLocaleString()}</p>}
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}

useBarterTrades

Fetch a list of trades for an address, optionally filtered by status.

import { useBarterTrades } from '@thebarmaeffect/barter-react';

Signature

function useBarterTrades(
  address: string | undefined,
  status?: number,
): UseBarterTradesResult

Parameters

NameTypeDescription
addressstring | undefinedFilter trades by this address. undefined to skip.
statusnumberOptional status filter (0-4). Omit for all trades.

Return Type

interface UseBarterTradesResult {
  data: Trade[];          // Array of trades (empty array when loading or no results)
  loading: boolean;       // true while fetching
  error: Error | null;    // Error if fetch failed
  refetch: () => void;    // Manual re-fetch trigger
}

Example

function TradeHistory({ address }: { address: string }) {
  const { data: trades, loading, error, refetch } = useBarterTrades(address);

  if (loading) return <TableSkeleton rows={5} />;
  if (error) return <p>Error: {error.message}</p>;

  const STATUS_LABELS = ['Proposed', 'Accepted', 'Settled', 'Disputed', 'Expired'];

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <h2>Trade History ({trades.length})</h2>
        <button onClick={refetch}>Refresh</button>
      </div>

      {trades.length === 0 ? (
        <p>No trades found.</p>
      ) : (
        <table>
          <thead>
            <tr>
              <th>ID</th>
              <th>Counterparty</th>
              <th>Amount</th>
              <th>Status</th>
              <th>Date</th>
            </tr>
          </thead>
          <tbody>
            {trades.map((trade) => (
              <tr key={trade.tradeId}>
                <td>{trade.tradeId.slice(0, 10)}...</td>
                <td>{trade.partyB.slice(0, 6)}...{trade.partyB.slice(-4)}</td>
                <td>{trade.amountSats.toLocaleString()} sats</td>
                <td>{STATUS_LABELS[trade.status]}</td>
                <td>{trade.createdAt.toLocaleDateString()}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

Filtering by Status

function ActiveTrades({ address }: { address: string }) {
  // Only fetch proposed trades (status = 0)
  const { data: proposed } = useBarterTrades(address, 0);

  // Only fetch accepted trades (status = 1)
  const { data: accepted } = useBarterTrades(address, 1);

  return (
    <div>
      <h3>Proposed ({proposed.length})</h3>
      <h3>Accepted ({accepted.length})</h3>
    </div>
  );
}

TypeScript Types

All types are exported from the package root:

import type {
  BarterConfig,
  BarterClientLike,
  UseBarterScoreResult,
  UseBarterProfileResult,
  TrustProfile,
  UseBarterTradeResult,
  Trade,
  UseBarterTradesResult,
} from '@thebarmaeffect/barter-react';

Error Handling

All hooks follow the same error pattern: errors are captured in the error field and never thrown to the component. This prevents unhandled promise rejections and makes error handling declarative.

Per-Hook Error Handling

function SafeScore({ address }: { address: string }) {
  const { data, loading, error } = useBarterScore(address);

  if (error) {
    return <div className="error-banner">{error.message}</div>;
  }

  // ...
}

Error Boundary Integration

For unexpected errors (e.g., from event handlers that use the client directly), use React Error Boundaries:

import { ErrorBoundary } from 'react-error-boundary';

function App() {
  return (
    <ErrorBoundary fallback={<div>Something went wrong</div>}>
      <BarterProvider config={{ rpcUrl: 'https://rpc.sepolia.org' }}>
        <Dashboard />
      </BarterProvider>
    </ErrorBoundary>
  );
}

Common Error Scenarios

Error MessageCauseResolution
Invalid address: 0x...Address failed ethers.isAddress() validationValidate address before passing to hook
Contract not initializedProvider config is missing or invalidEnsure BarterProvider has valid config
Network timeoutRPC endpoint is unreachableCheck RPC URL and network connectivity

Retry Pattern

function ScoreWithRetry({ address }: { address: string }) {
  const { data, loading, error, refetch } = useBarterScore(address);

  if (error) {
    return (
      <div>
        <p>Failed to load score: {error.message}</p>
        <button onClick={refetch}>Retry</button>
      </div>
    );
  }

  // ...
}

SSR Considerations

The React SDK is designed to work safely in server-side rendering environments (Next.js, Remix, etc.).

Key Behaviors During SSR

  • No window access -- Hooks use useState and useEffect, so no data is fetched during server rendering. The initial render always returns the loading/empty state.
  • No hydration mismatch -- Because hooks start in loading: true (or data: null) during both SSR and the initial client render, there is no mismatch.
  • Data fetches on the client -- All blockchain queries run exclusively in useEffect, which only fires on the client after hydration.
// Use 'use client' directive in Next.js App Router
'use client';

import { BarterProvider, useBarterScore } from '@thebarmaeffect/barter-react';

// This component renders a loading state on the server,
// then fetches data after hydration on the client.
function TrustDisplay({ address }: { address: string }) {
  const { data: score, loading } = useBarterScore(address);

  return (
    <div>
      {loading ? (
        <div className="skeleton" style={{ width: 60, height: 24 }} />
      ) : (
        <span>{score}</span>
      )}
    </div>
  );
}

Server-Side Data Fetching

For server-rendered trust data, use the core SDK (@thebarmaeffect/barter-sdk) directly in server components or getServerSideProps:

// Next.js App Router -- Server Component
import { BarterClient } from '@thebarmaeffect/barter-sdk';

const client = new BarterClient({ rpcUrl: process.env.RPC_URL });

export default async function TrustPage({ params }: { params: { address: string } }) {
  const profile = await client.trust.getProfile(params.address);

  return (
    <div>
      <h1>Trust Profile</h1>
      <p>Score: {profile.score}</p>
      <p>Trades: {profile.tradeCount}</p>
    </div>
  );
}

Framework Integration

Next.js App Router

// app/providers.tsx
'use client';

import { BarterProvider } from '@thebarmaeffect/barter-react';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <BarterProvider config={{
      rpcUrl: process.env.NEXT_PUBLIC_RPC_URL,
      network: 'sepolia',
    }}>
      {children}
    </BarterProvider>
  );
}

// app/layout.tsx
import { Providers } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

// app/trust/[address]/page.tsx
'use client';

import { useBarterProfile } from '@thebarmaeffect/barter-react';

export default function TrustPage({ params }: { params: { address: string } }) {
  const { data: profile, loading } = useBarterProfile(params.address);
  // ...
}

Next.js Pages Router

// pages/_app.tsx
import type { AppProps } from 'next/app';
import { BarterProvider } from '@thebarmaeffect/barter-react';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <BarterProvider config={{
      rpcUrl: process.env.NEXT_PUBLIC_RPC_URL,
      network: 'sepolia',
    }}>
      <Component {...pageProps} />
    </BarterProvider>
  );
}

Vite + React

// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BarterProvider } from '@thebarmaeffect/barter-react';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BarterProvider config={{
      rpcUrl: import.meta.env.VITE_RPC_URL,
      network: 'sepolia',
    }}>
      <App />
    </BarterProvider>
  </React.StrictMode>,
);

Remix

// app/root.tsx
import { BarterProvider } from '@thebarmaeffect/barter-react';

export default function App() {
  return (
    <html>
      <body>
        <BarterProvider config={{ rpcUrl: 'https://rpc.sepolia.org', network: 'sepolia' }}>
          <Outlet />
        </BarterProvider>
      </body>
    </html>
  );
}

Recipes

Trust Badge Component

A reusable badge that displays a color-coded trust score.

import { useBarterScore } from '@thebarmaeffect/barter-react';

function TrustBadge({ address }: { address: string }) {
  const { data: score, loading, error } = useBarterScore(address);

  if (loading) return <span className="badge badge-loading">...</span>;
  if (error) return <span className="badge badge-error">ERR</span>;
  if (score === null) return null;

  const level =
    score >= 80 ? 'high' :
    score >= 50 ? 'medium' :
    score >= 20 ? 'low' : 'minimal';

  return (
    <span className={`badge badge-trust-${level}`} title={`Trust score: ${score}`}>
      {score}
    </span>
  );
}

Trade History Table

import { useBarterTrades } from '@thebarmaeffect/barter-react';

function TradeTable({ address }: { address: string }) {
  const { data: trades, loading, error, refetch } = useBarterTrades(address);

  if (loading) return <p>Loading trades...</p>;
  if (error) return <p>Error: {error.message}</p>;
  if (trades.length === 0) return <p>No trades found for this address.</p>;

  return (
    <table>
      <thead>
        <tr>
          <th>Trade ID</th>
          <th>Amount (sats)</th>
          <th>Status</th>
        </tr>
      </thead>
      <tbody>
        {trades.map((t) => (
          <tr key={t.tradeId}>
            <td><code>{t.tradeId.slice(0, 12)}...</code></td>
            <td>{t.amountSats.toLocaleString()}</td>
            <td>{['Proposed', 'Accepted', 'Settled', 'Disputed', 'Expired'][t.status]}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Profile Card

import { useBarterProfile } from '@thebarmaeffect/barter-react';

function ProfileCard({ address }: { address: string }) {
  const { data: profile, loading } = useBarterProfile(address);

  if (loading || !profile) return <div className="card-skeleton" />;

  return (
    <div className="card">
      <div className="card-header">
        <h3>{address.slice(0, 6)}...{address.slice(-4)}</h3>
        <span className="score">{profile.score}</span>
      </div>
      <div className="card-body">
        <div className="stat">
          <span className="stat-label">Trades</span>
          <span className="stat-value">{profile.tradeCount}</span>
        </div>
        <div className="stat">
          <span className="stat-label">Completion</span>
          <span className="stat-value">{(profile.completionRate * 100).toFixed(0)}%</span>
        </div>
        <div className="stat">
          <span className="stat-label">Partners</span>
          <span className="stat-value">{profile.uniqueCounterparties}</span>
        </div>
      </div>
    </div>
  );
}

Polling for Updates

Use refetch with setInterval to poll for live updates:

import { useEffect } from 'react';
import { useBarterTrade } from '@thebarmaeffect/barter-react';

function LiveTradeStatus({ tradeId }: { tradeId: string }) {
  const { data: trade, refetch } = useBarterTrade(tradeId);

  useEffect(() => {
    // Poll every 10 seconds while trade is pending
    if (!trade || trade.status < 2) {
      const interval = setInterval(refetch, 10_000);
      return () => clearInterval(interval);
    }
  }, [trade, refetch]);

  if (!trade) return <span>Loading...</span>;

  const STATUS_LABELS = ['Proposed', 'Accepted', 'Settled', 'Disputed', 'Expired'];
  return <span>{STATUS_LABELS[trade.status]}</span>;
}

Development

Prerequisites

  • Node.js >= 18
  • React 18+

Setup

git clone https://github.com/TheBarmaEffect/barter-protocol.git
cd barter-protocol/sdk-react
npm install

Commands

# Build (CJS + ESM + types)
npm run build

# Run tests
npm test

# Type-check
npm run lint

Testing

Tests use Jest with React Testing Library:

npm test

Contributing

  • Fork the repository.
  • Create a feature branch: git checkout -b feat/my-hook
  • Make your changes and add tests.
  • Ensure tests pass: npm test
  • Ensure type-checking passes: npm run lint
  • Submit a pull request.

License

MIT -- see LICENSE for details.

Keywords

barter

FAQs

Package last updated on 11 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts