Firma del lado del cliente
Crea y envía transacciones en el navegador con kit + adaptadores de wallet.
Receta
"use client";
import { createSolanaRpc, signTransactionMessageWithSigners } from "@solana/kit";Cuándo usar esto:
- Transacciones pagadas por el usuario y acuñación de NFTs.
- Canjes de DeFi que requieren aprobación de la wallet.
- Cualquier acción que necesite claves privadas del usuario.
Ejemplo de trabajo
"use client";
import { useCallback } from "react";
import { useWallet } from "@solana/wallet-adapter-react";
import {
createSolanaRpc,
createTransactionMessage,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signTransactionMessageWithSigners,
sendAndConfirmTransactionFactory,
createSolanaRpcSubscriptions,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
export function SendButton({ to, lamports }: { to: string; lamports: bigint }) {
const wallet = useWallet();
const rpc = createSolanaRpc(process.env.NEXT_PUBLIC_RPC_URL!);
const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.NEXT_PUBLIC_RPC_WSS_URL!);
const onSend = useCallback(async () => {
if (!wallet.publicKey) return;
const { value: bh } = await rpc.getLatestBlockhash().send();
// Puente de la wallet al firmante de kit (usa el helper de tu plantilla)
let msg = createTransactionMessage({ version: 0 });
// Establece el firmante del pagador de tarifas, la vida útil, la instrucción de transferencia...
const signed = await signTransactionMessageWithSigners(msg);
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
await sendAndConfirm(signed, { commitment: "confirmed" });
}, [wallet, rpc, rpcSubscriptions, to, lamports]);
return <button onClick={onSend}>Enviar</button>;
}Lo que esto demuestra:
- Todo el pipeline de firma se ejecuta solo en el cliente.
- Blockhash nuevo por clic.
- Confirmación a través de la fábrica WSS.
Análisis profundo
Cómo funciona
- El mensaje se construye en el navegador; la wallet firma sin exportar secretos.
- Simulación, un paso opcional antes del modal de la wallet.
- Next.js no serializa bytes de transacción a través del servidor para flujos de autocustodia.
Errores comunes
- Intentar usar la wallet de la ventana en una Server Action - falla. Solución: mantenerla en un componente cliente.
- Cierre obsoleto en la wallet - enviar después de desconectar. Solución: proteger con el estado de conectado.
- Falta de entorno WSS - la confirmación se cuelga. Solución: proporcionar URL WSS pública.
- Expiración del blockhash durante la aprobación de la wallet - reconstruir y reintentar.
- No deshabilitar el botón mientras está pendiente - intentos de doble gasto.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
| Transacción patrocinada por el servidor | Onboarding sin gas | Autocustodia completa |
| Wallet incrustada | Experiencia Web2 | Usuarios avanzados |
Preguntas frecuentes
¿App Router?
Proveedores en layout.tsx del cliente; las páginas mezclan componentes de servidor y cliente.
¿Secretos?
Entorno de servidor solo para keypairs; NEXT_PUBLIC_ para URLs RPC.
¿kit 7.0.0?
Todos los ejemplos usan @solana/kit, no web3.js v1.
¿Wallet?
Firma solo en componentes cliente.
¿Caché?
Revalida las lecturas on-chain con cuidado: la cadena es la fuente de verdad.
¿Acciones?
Endpoints HTTPS que devuelven JSON de Acciones de Solana.
¿Móvil?
Aplicación React Native separada con MWA.
¿Pruebas?
Surfpool para integración; simular RPC en pruebas unitarias.
¿Mainnet?
Archivos de entorno y IDs de programa separados.
¿Errores?
Decodificar registros de simulación para mensajes dirigidos al usuario.
Relacionado
- Firmar transacciones y mensajes - firma de wallet
- Experiencia de usuario de transacciones - feedback
- Construir mensajes de transacción - componer
Versiones de Stack: Esta página se escribió para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, y LiteSVM 0.6.x.