
Research
/Security News
Popular Rust Crates Compromised in Build-Time Supply Chain Attack
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.
@thebarmaeffect/barter-react
Advanced tools
React hooks and components for the BARTER Proof of Trade Protocol
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.
{ data, loading, error, refetch }.refetch() function for manual re-queries.window access during SSR.# npm
npm install @thebarmaeffect/barter-react react ethers
# yarn
yarn add @thebarmaeffect/barter-react react ethers
# pnpm
pnpm add @thebarmaeffect/barter-react react ethers
| Package | Version | Purpose |
|---|---|---|
react | ^18.0.0 | React framework |
ethers | ^6.0.0 | Ethereum provider and address validation |
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>;
}
BarterProviderContext provider that makes BARTER configuration available to all descendant hooks.
import { BarterProvider } from '@thebarmaeffect/barter-react';
<BarterProvider config={config}>
{children}
</BarterProvider>
| Prop | Type | Required | Description |
|---|---|---|---|
config | BarterConfig | No | RPC and network configuration |
children | React.ReactNode | Yes | Child components that can use BARTER hooks |
BarterConfiginterface BarterConfig {
rpcUrl?: string; // JSON-RPC endpoint URL (e.g., 'https://rpc.sepolia.org')
network?: string; // Network name (e.g., 'sepolia')
}
// 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>
);
}
useBarterConfigAccess 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>;
}
function useBarterConfig(): BarterConfig
Returns: BarterConfig -- The configuration object from the nearest provider, or {} if no provider is found.
useBarterClientGet the low-level BARTER client instance for direct API access. Returns null if the provider has no configuration.
import { useBarterClient } from '@thebarmaeffect/barter-react';
function useBarterClient(): BarterClientLike | null
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.
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>;
}
useBarterScoreFetch the trust score for an Ethereum address. Automatically fetches on mount and when the address changes.
import { useBarterScore } from '@thebarmaeffect/barter-react';
function useBarterScore(address: string | undefined): UseBarterScoreResult
| Name | Type | Description |
|---|---|---|
address | string | undefined | Ethereum address to query. Pass undefined to skip fetching. |
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
}
address is defined, begins fetching immediately.undefined: Resets data to null, loading to false, error to null.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>
);
}
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>
);
}
useBarterProfileFetch the full trust profile for an Ethereum address, including score, trade count, completion rate, counterparty count, and timestamps.
import { useBarterProfile } from '@thebarmaeffect/barter-react';
function useBarterProfile(address: string | undefined): UseBarterProfileResult
| Name | Type | Description |
|---|---|---|
address | string | undefined | Ethereum address to query. Pass undefined to skip. |
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
}
TrustProfileinterface 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
}
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>
);
}
useBarterTradeFetch the details of a single trade by its ID.
import { useBarterTrade } from '@thebarmaeffect/barter-react';
function useBarterTrade(tradeId: string | undefined): UseBarterTradeResult
| Name | Type | Description |
|---|---|---|
tradeId | string | undefined | Trade ID to query. Pass undefined to skip. |
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
}
Tradeinterface 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
}
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>
);
}
useBarterTradesFetch a list of trades for an address, optionally filtered by status.
import { useBarterTrades } from '@thebarmaeffect/barter-react';
function useBarterTrades(
address: string | undefined,
status?: number,
): UseBarterTradesResult
| Name | Type | Description |
|---|---|---|
address | string | undefined | Filter trades by this address. undefined to skip. |
status | number | Optional status filter (0-4). Omit for all trades. |
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
}
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>
);
}
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>
);
}
All types are exported from the package root:
import type {
BarterConfig,
BarterClientLike,
UseBarterScoreResult,
UseBarterProfileResult,
TrustProfile,
UseBarterTradeResult,
Trade,
UseBarterTradesResult,
} from '@thebarmaeffect/barter-react';
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.
function SafeScore({ address }: { address: string }) {
const { data, loading, error } = useBarterScore(address);
if (error) {
return <div className="error-banner">{error.message}</div>;
}
// ...
}
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>
);
}
| Error Message | Cause | Resolution |
|---|---|---|
Invalid address: 0x... | Address failed ethers.isAddress() validation | Validate address before passing to hook |
Contract not initialized | Provider config is missing or invalid | Ensure BarterProvider has valid config |
| Network timeout | RPC endpoint is unreachable | Check RPC URL and network connectivity |
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>
);
}
// ...
}
The React SDK is designed to work safely in server-side rendering environments (Next.js, Remix, etc.).
window access -- Hooks use useState and useEffect, so no data is fetched during server rendering. The initial render always returns the loading/empty state.loading: true (or data: null) during both SSR and the initial client render, there is no mismatch.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>
);
}
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>
);
}
// 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);
// ...
}
// 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>
);
}
// 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>,
);
// 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>
);
}
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>
);
}
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>
);
}
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>
);
}
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>;
}
git clone https://github.com/TheBarmaEffect/barter-protocol.git
cd barter-protocol/sdk-react
npm install
# Build (CJS + ESM + types)
npm run build
# Run tests
npm test
# Type-check
npm run lint
Tests use Jest with React Testing Library:
npm test
git checkout -b feat/my-hooknpm testnpm run lintMIT -- see LICENSE for details.
FAQs
React hooks and components for the BARTER Proof of Trade Protocol
The npm package @thebarmaeffect/barter-react receives a total of 2 weekly downloads. As such, @thebarmaeffect/barter-react popularity was classified as not popular.
We found that @thebarmaeffect/barter-react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.

Research
/Security News
Socket uncovered 77 linked Firefox extensions, including 40 that steal wallet secrets or credentials and 37 deceptive sports-score shells.

Security News
NIST disclosed an unreleased AI tool called V-etalon and opened a broad inquiry into NVD modernization after years of automation plans produced no public enrichment system.