The @abstraxn/signer-react and @abstraxn/signer-core packages help you add embedded and external wallet onboarding to your dApp.

1. Installation

Install both SDK packages using your preferred package manager:
npm i @abstraxn/signer-react @abstraxn/signer-core

2. Get Your API Key

  1. Visit https://dashboard.abstraxn.com/
  2. Log in or sign up
  3. Go to Apps and click Create New App
  4. Navigate to Wallets and click Add Wallet Service
  5. Click View Details and copy your API key
Keep your API key in environment variables. Do not hardcode it in client code.

3. Setup Provider

Wrap your app with AbstraxnProvider:
import { AbstraxnProvider } from "@abstraxn/signer-react";

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <AbstraxnProvider
      config={{
        apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY || "YOUR_API_KEY",
      }}
    >
      {children}
    </AbstraxnProvider>
  );
}

3b. Enable Solana Gasless Transactions (Optional)

If your app supports Solana, you can enable gasless transactions by adding the solanaGasless option to your provider config. This uses the same gas sponsorship model as the Solana Relayer — fees are paid from your paymaster gas pool.
import { AbstraxnProvider } from "@abstraxn/signer-react";

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <AbstraxnProvider
      config={{
        apiKey: process.env.NEXT_PUBLIC_ABSTRAXN_API_KEY || "YOUR_API_KEY",
        solanaGasless: {
          enabled: true,
          relayerUrl: process.env.NEXT_PUBLIC_SOLANA_RELAYER_URL!,
          // e.g. "https://solana-paymaster.abstraxn.com/api/v1/103/?apikey=your_api_key"
        },
      }}
    >
      {children}
    </AbstraxnProvider>
  );
}
To get your Solana relayer URL, go to your Abstraxn DashboardPaymaster — if your app has Solana selected, you will see the Solana paymaster link.

4. Connect Wallet

Use the built-in connect UI:
import { ConnectButton } from "@abstraxn/signer-react";

export function WalletButton() {
  return <ConnectButton connectText="Connect Wallet" />;
}

5. Send a Transaction (Current Flow)

Use usePrepareRawTxn -> useSignAndSendTxn -> useWaitForTxnReceipt.
import { useState } from "react";
import {
  useAddress,
  useIsConnected,
  usePublicClient,
  usePrepareRawTxn,
  useSignAndSendTxn,
  useWaitForTxnReceipt,
  useEstimateGas,
  useGetGasPrice,
} from "@abstraxn/signer-react";
import { polygonAmoy } from "viem/chains";
import { parseEther } from "viem";

export function SendTransaction() {
  const address = useAddress();
  const isConnected = useIsConnected();
  const [sending, setSending] = useState(false);
  const [hash, setHash] = useState<`0x${string}` | null>(null);

  const { publicClient } = usePublicClient(
    polygonAmoy,
    "https://rpc-amoy.polygon.technology"
  );
  const { prepareRawTxn } = usePrepareRawTxn(publicClient);
  const { signAndSendTxn } = useSignAndSendTxn(publicClient);
  const { waitForTxnReceipt } = useWaitForTxnReceipt(publicClient);
  const { estimateGas } = useEstimateGas(publicClient);
  const { getGasPrice } = useGetGasPrice(publicClient);

  const handleSend = async () => {
    if (!isConnected || !address) return;
    setSending(true);
    try {
      const rawTx = await prepareRawTxn({
        from: address as `0x${string}`,
        to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
        value: parseEther("0.001"),
      });

      const { gasLimit } = await estimateGas({
        account: address as `0x${string}`,
        to: rawTx.to,
        data: rawTx.data,
        value: rawTx.value,
      });
      const fees = await getGasPrice();

      const result = await signAndSendTxn({
        from: address as `0x${string}`,
        ...rawTx,
        gas: {
          gasLimit,
          maxFeePerGas: fees.maxFeePerGas,
          maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
          gasPrice: fees.gasPrice,
        },
      });

      setHash(result.hash as `0x${string}`);
      await waitForTxnReceipt({ hash: result.hash as `0x${string}`, confirmations: 1 });
    } finally {
      setSending(false);
    }
  };

  return (
    <button onClick={handleSend} disabled={!isConnected || sending}>
      {sending ? "Sending..." : "Send 0.001 MATIC"}
    </button>
  );
}