Stellar Wallet Kit - Integración rápida

Construyendo un Sistema de Gestión de Carteras Stellar

Esta guía te mostrará cómo crear un sistema integral de gestión de carteras para la integración con la blockchain Stellar en tu aplicación React/Next.js. El sistema ofrece conexión segura de carteras, desconexión y gestión de estado con persistencia.

Resumen

El sistema consta de tres componentes principales:

  1. Proveedor de Carteras - Gestión global del estado para la información de la cartera

  2. Hook de Cartera - Lógica de negocio para las operaciones de la cartera

  3. Configuración del Kit de Carteras - Configuración de integración con la blockchain Stellar

Paso 1: Instalar dependencias

Primero, instala el paquete requerido del Stellar Wallet Kit:

npm install @creit.tech/stellar-wallets-kit

Paso 2: Configurar el Stellar Wallet Kit

Crea un archivo de configuración que configure el Stellar Wallet Kit con soporte para múltiples tipos de cartera:

import {
  StellarWalletsKit,
  WalletNetwork,
  FREIGHTER_ID,
  AlbedoModule,
  FreighterModule,
} from "@creit.tech/stellar-wallets-kit";

/**
 * Configuración principal para el Stellar Wallet Kit
 * Este kit admite múltiples tipos de carteras, incluyendo Freighter y Albedo
 * Configurar para TESTNET durante el desarrollo y MAINNET para producción
 */
export const kit: StellarWalletsKit = new StellarWalletsKit({
  network: WalletNetwork.TESTNET,
  selectedWalletId: FREIGHTER_ID,
  modules: [new FreighterModule(), new AlbedoModule()],
});

/**
 * Interfaz para los parámetros de firma de transacciones
 */
interface signTransactionProps {
  unsignedTransaction: string;
  address: string;
}

/**
 * Firmar una transacción Stellar usando la cartera conectada
 * Esta función maneja el proceso de firma y devuelve la transacción firmada
 * 
 * @param unsignedTransaction - La cadena XDR de la transacción sin firmar
 * @param address - La dirección de la cartera que firmará la transacción
 * @returns Promise<string> - El XDR de la transacción firmada
 */
export const signTransaction = async ({
  unsignedTransaction,
  address,
}: signTransactionProps): Promise<string> => {
  const { signedTxXdr } = await kit.signTransaction(unsignedTransaction, {
    address,
    networkPassphrase: WalletNetwork.TESTNET,
  });

  return signedTxXdr;
};

Paso 3: Crear el Proveedor de Contexto de la Cartera

El Proveedor de Carteras gestiona el estado global de la cartera y persiste la información en localStorage para una mejor experiencia de usuario

"use client";

import {
  createContext,
  useContext,
  useState,
  useEffect,
  ReactNode,
} from "react";

/**
 * Definición de tipos para el contexto de la cartera
 * Contiene la dirección de la cartera, el nombre y funciones para gestionar el estado de la cartera
 */
type WalletContextType = {
  walletAddress: string | null;
  walletName: string | null;
  setWalletInfo: (address: string, name: string) => void;
  clearWalletInfo: () => void;
};

/**
 * Crear el contexto de React para la gestión del estado de la cartera
 */
const WalletContext = createContext<WalletContextType | undefined>(undefined);

/**
 * Componente Wallet Provider que envuelve la aplicación
 * Gestiona el estado de la cartera y proporciona información de la cartera a los componentes hijos
 * Carga automáticamente la información de la cartera guardada desde localStorage en la inicialización
 */
export const WalletProvider = ({ children }: { children: ReactNode }) => {
  const [walletAddress, setWalletAddress] = useState<string | null>(null);
  const [walletName, setWalletName] = useState<string | null>(null);

  /**
   * Cargar la información de la cartera guardada desde localStorage cuando el componente se monta
   * Esto asegura que el estado de la cartera persista a través de las sesiones del navegador
   */
  useEffect(() => {
    const storedAddress = localStorage.getItem("walletAddress");
    const storedName = localStorage.getItem("walletName");

    if (storedAddress) setWalletAddress(storedAddress);
    if (storedName) setWalletName(storedName);
  }, []);

  /**
   * Establecer la información de la cartera y guardarla en localStorage
   * Esta función se llama cuando una cartera se conecta con éxito
   * 
   * @param address - La dirección pública de la cartera
   * @param name - El nombre/identificador de la cartera (por ejemplo, "Freighter", "Albedo")
   */
  const setWalletInfo = (address: string, name: string) => {
    setWalletAddress(address);
    setWalletName(name);
    localStorage.setItem("walletAddress", address);
    localStorage.setItem("walletName", name);
  };

  /**
   * Borrar la información de la cartera y eliminarla de localStorage
   * Esta función se llama al desconectar una cartera
   */
  const clearWalletInfo = () => {
    setWalletAddress(null);
    setWalletName(null);
    localStorage.removeItem("walletAddress");
    localStorage.removeItem("walletName");
  };

  return (
    <WalletContext.Provider
      value={{ walletAddress, walletName, setWalletInfo, clearWalletInfo }}
    >
      {children}
    </WalletContext.Provider>
  );
};

/**
 * Hook personalizado para acceder al contexto de la cartera
 * Proporciona el estado de la cartera y funciones a los componentes
 * Lanza un error si se usa fuera de WalletProvider
 */
export const useWalletContext = () => {
  const context = useContext(WalletContext);
  if (!context) {
    throw new Error("useWalletContext must be used within WalletProvider");
  }
  return context;
};

Paso 4: Crear el Hook de la Cartera

El hook de la cartera encapsula toda la lógica de negocio para las operaciones de la cartera, proporcionando una interfaz limpia para los componentes:

import { kit } from "@/config/wallet-kit";
import { useWalletContext } from "@/providers/wallet.provider";
import { ISupportedWallet } from "@creit.tech/stellar-wallets-kit";

/**
 * Hook personalizado que proporciona funcionalidad de conexión y desconexión de carteras
 * Se integra con el Stellar Wallet Kit y gestiona el estado de la cartera a través del contexto
 */
export const useWallet = () => {
  // Obtener funciones de gestión de cartera desde el contexto
  const { setWalletInfo, clearWalletInfo } = useWalletContext();

  /**
   * Conectar a una cartera Stellar usando el Wallet Kit
   * Abre un modal para la selección de cartera y maneja el proceso de conexión
   * Establece automáticamente la información de la cartera en el contexto tras una conexión exitosa
   */
  const connectWallet = async () => {
    await kit.openModal({
      modalTitle: "Conéctate a tu cartera favorita",
      onWalletSelected: async (option: ISupportedWallet) => {
        // Establecer la cartera seleccionada como la cartera activa
        kit.setWallet(option.id);

        // Obtener la dirección y el nombre de la cartera
        const { address } = await kit.getAddress();
        const { name } = option;

        // Almacenar la información de la cartera en el contexto y en localStorage
        setWalletInfo(address, name);
      },
    });
  };

  /**
   * Desconectarse de la cartera actual
   * Borra la información de la cartera del contexto y de localStorage
   * Desconecta la cartera del Stellar Wallet Kit
   */
  const disconnectWallet = async () => {
    await kit.disconnect();
    clearWalletInfo();
  };

  /**
   * Manejar la conexión de la cartera con manejo de errores
   * Envuelve la función connectWallet en un bloque try-catch para un mejor manejo de errores
   */
  const handleConnect = async () => {
    try {
      await connectWallet();
    } catch (error) {
      console.error("Error connecting wallet:", error);
      // Aquí puedes añadir manejo adicional de errores, como mostrar notificaciones al usuario
    }
  };

  /**
   * Manejar la desconexión de la cartera con manejo de errores
   * Envuelve la función disconnectWallet en un bloque try-catch para un mejor manejo de errores
   */
  const handleDisconnect = async () => {
    try {
      await disconnectWallet();
    } catch (error) {
      console.error("Error disconnecting wallet:", error);
      // Aquí puedes añadir manejo adicional de errores, como mostrar notificaciones al usuario
    }
  };

  return {
    connectWallet,
    disconnectWallet,
    handleConnect,
    handleDisconnect,
  };
};

Paso 5: Integrar el Proveedor en tu App

Envuelve tu aplicación con WalletProvider para habilitar la gestión del estado de la cartera en toda tu app:

import { WalletProvider } from "@/providers/wallet.provider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <WalletProvider>
          {children}
        </WalletProvider>
      </body>
    </html>
  );
}

Paso 6: Usar el Hook de Cartera en Componentes

Ahora puedes usar la funcionalidad de la cartera en cualquier componente. Aquí hay un ejemplo de un botón de conexión de cartera:

import { useWallet } from "@/hooks/wallet.hook";
import { useWalletContext } from "@/providers/wallet.provider";

/**
 * Componente de botón para conectar/desconectar la cartera
 * Muestra diferentes estados según el estado de conexión de la cartera
 */
export const WalletButton = () => {
  const { handleConnect, handleDisconnect } = useWallet();
  const { walletAddress, walletName } = useWalletContext();

  // Si la cartera está conectada, mostrar la opción de desconexión
  if (walletAddress) {
    return (
      <div className="flex items-center gap-4">
        <div className="text-sm">
          <p className="font-medium">Conectado: {walletName}</p>
          <p className="text-gray-500">{walletAddress}</p>
        </div>
        <button 
          onClick={handleDisconnect}
          className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
        >
          Desconectar
        </button>
      </div>
    );
  }

  // Si la cartera no está conectada, mostrar la opción de conexión
  return (
    <button 
      onClick={handleConnect}
      className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
    >
      Conectar Billetera
    </button>
  );
};

Características clave y beneficios

Persistencia de estado

  • La información de la cartera se guarda automáticamente en localStorage

  • Los usuarios no necesitan reconectar su cartera cada vez que visitan tu app

  • Experiencia de usuario fluida a través de las sesiones del navegador

Soporte multi-cartera

  • Admite múltiples tipos de carteras Stellar (Freighter, Albedo, etc.)

  • Fácil de agregar nuevos módulos de cartera a medida que estén disponibles

  • Los usuarios pueden elegir su cartera preferida

Manejo de errores

  • Manejo de errores integral para las operaciones de cartera

  • Recuperaciones elegantes cuando las operaciones de cartera fallan

  • Fácil de extender con notificaciones de error personalizadas

Seguridad de tipos

  • Soporte completo de TypeScript con definiciones de tipos adecuadas

  • Soporte de IntelliSense para una mejor experiencia de desarrollo

  • Verificación de errores en tiempo de compilación para las operaciones de cartera

Arquitectura basada en contexto

  • Separación clara de responsabilidades entre la gestión del estado y la lógica de negocio

  • Fácil acceso al estado de la cartera desde cualquier componente

  • Gestión centralizada del estado de la cartera

Opciones de configuración

Selección de red

  • TESTNET: Usar durante el desarrollo y las pruebas

  • MAINNET: Usar para aplicaciones en producción

Última actualización

¿Te fue útil?