Skip to content

Latest commit

 

History

24 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@1claw/wallet-react

⭐ Star 1clawAI/agent-templates — ready-to-run agent templates wired to 1Claw. It is our single starred repo.

React components for embedding 1Claw treasury wallets in your app.

Built for platform developers using the 1Claw Platform API (plt_ keys). Your users get multi-chain wallets, send/swap/receive flows, social login, email OTP, and passkey transaction auth without you running wallet infrastructure or storing private keys.

Custody, stated plainly: the wallets this package renders are custody: server — 1Claw holds the KMS-wrapped key and signs after your spend policies, step-up and the sanctions screen. Every wallet object carries custody, so read it before making a non-custodial claim to your users. Self-custody models (passkey-owned Safes on EVM, 2-of-2 threshold keys on Solana) exist in the 1Claw dashboard today and are not yet wired into these components; see Custody.

Installation

npm install @1claw/wallet-react

Quick Start

import { OneclawEmbeddedWallet } from "@1claw/wallet-react";

function App() {
  return (
    <OneclawEmbeddedWallet
      appId="your-platform-slug"
      socialProviders={["google", "apple"]}
      socialProviderConfig={{
        google: { clientId: "your-google-client-id" },
      }}
      features={{ send: true, swap: true, buy: true, receive: true }}
      onLogin={(user) => console.log(user.walletAddress)}
    />
  );
}

Components

<OneclawEmbeddedWallet />

Full embedded wallet with social login, email OTP login, Send/Swap/Receive/Buy views. Internally wraps itself in <OneclawWalletProvider>.

v0.5.1: Passkey registration nudge on Send/Swap when HFA requires passkey_only or passkey_required and the user has no registered passkeys — inline Register passkey CTA instead of failing at submit time.

v0.55 note: Graduated HITL and extended transaction guardrails (tx_approval_policy, org freeze, etc.) are configured on agents via the Vault API / dashboard — not on embedded wallet send/swap flows.

Prop Type Required Description
appId string Yes Platform app slug
baseUrl string No API base URL (default: https://api.1claw.co)
theme "light" | "dark" | "auto" No Color theme
brandColor string No Primary brand color hex
chains string[] No Chains to provision wallets for
features { send?, swap?, buy?, receive? } No Toggle features
socialProviders ("google" | "apple" | "discord")[] No Enabled social login providers
socialProviderConfig SocialProviderConfig No OAuth client IDs per provider
persistSession "session" | "local" | false No Token persistence strategy (default: "session")
onLogin (user) => void No Login success callback
onLogout () => void No Logout callback
onTransactionSent (result) => void No Send success callback
onSwapCompleted (result) => void No Swap success callback
onError (error: Error) => void No Error callback

<OneclawTreasuryWidget />

Read-only balance widget. Displays wallet balances for each chain, fetched on mount.

Prop Type Required Description
apiKey string Yes Platform API key (plt_...) or session token
baseUrl string No API base URL (default: https://api.1claw.co)
theme "light" | "dark" | "auto" No Color theme
onError (error: Error) => void No Error callback
className string No CSS class for outer container

Note: onTransactionSent and onSwapCompleted props are accepted but not currently wired to any UI within TreasuryWidget. Use <OneclawEmbeddedWallet> for full send/swap functionality.

<OneclawWalletProvider />

Lower-level provider for building custom UI:

import { OneclawWalletProvider, useOneclawWallet } from "@1claw/wallet-react";

function CustomWalletUI() {
  const { wallets, balances, send, swap, refreshBalance, loginWithEmailOtp, logout } = useOneclawWallet();
  // Build your own UI...
}

function App() {
  return (
    <OneclawWalletProvider apiKey="plt_..." appId="your-slug" persistSession="session">
      <CustomWalletUI />
    </OneclawWalletProvider>
  );
}

<SignInWith1Claw />

Drop-in "Sign in with 1Claw" OAuth button:

import { SignInWith1Claw, handleSignInCallback } from "@1claw/wallet-react/oauth";

function LoginPage() {
  return (
    <SignInWith1Claw
      clientId="your-app-slug"
      redirectUri="https://yourapp.com/auth/callback"
      authorizeBaseUrl="https://1claw.co"
      apiBaseUrl="https://api.1claw.co"
    />
  );
}

// On your callback page:
const tokens = await handleSignInCallback({
  code: urlParams.get("code")!,
  state: urlParams.get("state")!,
  clientId: "your-app-slug",
  redirectUri: "https://yourapp.com/auth/callback",
  apiBaseUrl: "https://api.1claw.co",
});

<OneclawProposalDetail />

A wallet-style view of one agent signing request (a "proposal"): the scheme (message / typed data / digest / transaction), which agent asked, exactly what is being signed, expiry, and Sign / Reject. Pending proposals poll for status; approved ones show the "signature created" banner. Tier-2 signatures need a re-auth token — return one from getReauthToken (POST /v1/auth/reauth, purpose approval.decide).

import { OneclawProposalDetail, useOneclawWallet } from "@1claw/wallet-react";

function Proposals() {
  const { client } = useOneclawWallet();
  const [items, setItems] = useState([]);
  useEffect(() => { client.listProposals("pending").then(setItems); }, [client]);
  return items.map((p) => (
    <OneclawProposalDetail key={p.id} proposal={p} onDecided={() => client.listProposals("pending").then(setItems)} />
  ));
}

client.listProposals(status), client.getProposal(id) and client.decideProposal(id, decision, { reauthToken }) are the underlying calls; describeProposal(p) gives you the parsed fields if you render your own.

Hooks

useOneclawWallet()

Returns:

Field Type Description
wallets WalletInfo[] Active wallets
balances Record<string, WalletBalance> Cached balances by chain
loading boolean Initial load state
error Error | null Last error
client OneclawWalletClient Underlying API client
refreshWallets() () => Promise<void> Refetch wallet list
refreshBalance(chain) (chain: string) => Promise<WalletBalance | null> Fetch balance
generateWallets(chains?) (chains?: string[]) => Promise<WalletInfo[]> Generate new wallets
send(params) (params) => Promise<SendTransactionResult> Send a transaction
swap(params) (params) => Promise<SwapResult> Swap tokens via DEX aggregator
loginWithEmailOtp(email, code, chains?) (...) => Promise<SocialLoginResult> Verify OTP and authenticate
loginWithSocial(provider, idToken, chains?, redirectUri?) (...) => Promise<SocialLoginResult> Social login
logout() () => void Clear token and reset state

Theming

The widget uses CSS custom properties scoped to .ocw-embedded-wallet. You can customize the theme via:

  1. Props — theme and brandColor on the component
  2. ThemeConfig — Pass a full config to buildRootStyle() for per-instance scoping:
import { buildRootStyle } from "@1claw/wallet-react";

const style = buildRootStyle("dark", {
  brandColor: "#8b5cf6",
  borderRadius: "16px",
  fontFamily: "Inter, sans-serif",
  cssVars: { "--ocw-bg": "#1a1a2e" },
});

Next.js App Router

This library does not include "use client" directives. If you're using Next.js App Router, wrap the component in your own client boundary:

"use client";
import { OneclawEmbeddedWallet } from "@1claw/wallet-react";
export default function WalletPage() {
  return <OneclawEmbeddedWallet appId="..." />;
}

Passkey enrollment (passwordless / HFA)

Social and email-OTP users often have no password. When your platform spend policy sets passkey_only for send/swap, users must register a passkey before transacting.

v0.5.1: If HFA requires passkey and registered_passkeys === 0, Send and Swap views show an inline Register passkey button before submit (no silent failure at tx time).

const { registerPasskey, getEffectiveAuthPolicy, send } = useOneclawWallet();

const auth = await getEffectiveAuthPolicy();
if (auth.registered_passkeys === 0 && auth.policy.send === "passkey_only") {
  await registerPasskey("My device");
}

// send() auto-routes to passkey tx-assert when policy requires it
await send({ chain: "ethereum", to: "0x...", valueWei: "1000000000000000" });

Backend endpoints: POST /v1/auth/passkeys/register/begin|complete, GET /v1/treasury/wallets/auth-policy.

Security

  • The apiKey is a Platform API key, not a user key — it inherits platform custody guarantees.
  • Sends require password re-authentication or a passkey token.
  • The widget never stores or caches private keys client-side.
  • Session tokens are stored in sessionStorage by default (configurable via persistSession).

Subpath Exports

import { OneclawWalletClient } from "@1claw/wallet-react/client";
import { SignInWith1Claw } from "@1claw/wallet-react/oauth";

About

Embeddable treasury wallet React component for 1Claw Platform API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages