Un programa nativo en Solana es un crate de Rust que depende de solana-program, tiene como objetivo SBF como cdylib, y expone un punto de entrada que recibe un ID de programa, un slice de AccountInfos y datos de instrucción. No hay macro de struct de cuentas, ni generación automática de restricciones, ni IDL incorporado. Eres dueño del camino completo desde los bytes de entrada hasta los cambios de estado de salida.
Esta página es el mapa de la sección. Úsala para ubicar la distribución del punto de entrada, la validación manual, la (des)serialización, la lectura/escritura, la creación de cuentas CPI, el registro y las compensaciones de frameworks en un continuo antes de seguir las páginas de recetas enfocadas.
Los programas nativos de Solana son módulos SBF ordinarios. El runtime llama a process_instruction una vez por instrucción, te entrega solo lo que la transacción declaró, y espera Ok(()) o un ProgramError.
Sin Anchor, tres tareas recaen en tu código. Analizar los datos de instrucción en un enum estable o un diseño de opcode. Validar cada cuenta que el manejador utiliza: firmantes, capacidad de escritura, propietarios, igualdad de claves, semillas de PDA y relaciones comerciales. Modificar los bytes de las cuentas propiedad del programa bajo las reglas de préstamo de Solana, incluida la CPI del Sistema de Programas cuando debe existir una nueva cuenta.
Esas tareas son también la principal superficie de riesgo. Una verificación is_signer faltante, una suposición de propietario incorrecta o una ruta de reinicialización en una cuenta existente es un error crítico. Los frameworks codifican las mismas reglas en macros; el código nativo las hace visibles y fáciles de omitir.
Elige nativo cuando necesites control explícito, binarios más pequeños, CU base más bajo, diseños inusuales o verificaciones visibles en auditorías. Elige Anchor 0.32.1 (o un wrapper delgado como Pinocchio) cuando la velocidad, la generación de IDL/cliente y las restricciones declarativas importen más. Muchos equipos prototipan en Anchor, y luego reescriben las rutas críticas como programas nativos compuestos a través de CPI.
Nativo significa que programas contra la API solana-program sin la capa #[program] / #[account] de Anchor. Todavía despliegas un .so, pagas tarifas en lamports, declaras metadatos de cuenta desde clientes como @solana/kit 7.0.0, y construyes con herramientas SBF de Agave 4.1.1 / Solana CLI 3.0.10. Nativo es una capa de aplicación más delgada, no una cadena diferente.
La clave pública de tu programa; verificaciones de propietario y derivación de PDA
accounts: &[AccountInfo]
Cuentas que el cliente listó, con indicadores de firmante/escritura verificados por el runtime
instruction_data: &[u8]
Bytes opacos que decodificas en un enum de instrucción u opcode
El runtime no re-deriva la intención de tus tipos de Rust. Una cuenta incorrecta en la ranura 2, datos cortos o un diseño incorrecto deben fallar de forma segura en tu manejador.
AccountInfo expone clave, lamports, datos, propietario, ejecutable, is_signer e is_writable. Esos indicadores reflejan lo que la transacción declaró y el runtime verificó para las firmas. No prueban "esta es la bóveda para este usuario" o "este es el estado seguro del programa". Esa política es tuya: comparaciones, verificaciones de PDA e inspección de campos dentro de los datos.
Diseño típico: cdylib, entrypoint!(process_instruction), módulos para instruction, processor, state, y error, más solana-program y un serializador (a menudo Borsh). Un punto de entrada por binario; múltiples tipos de instrucciones viven detrás de una coincidencia (match) en datos analizados. Fundamentos de Programas Nativos recorre el esqueleto con ejemplos.
Cliente (@solana/kit / CLI / llamador de CPI) | Lista AccountMeta + instruction_data | firmar (o invoke_signed desde el llamador) v Runtime: cargar cuentas, verificar firmas y metadatos de escritura | v entrypoint! -> process_instruction(program_id, accounts, data) | | 1. deserializar instruction_data | 2. next_account_info / orden fijo de cuentas | 3. validar firmantes, propietarios, PDAs, relaciones | 4. tomar prestado/modificar datos, o CPI System Program | 5. msg! opcional / sol_log_data v Ok(()) confirma | Err(ProgramError) aborta la instrucción
Valida antes de los efectos secundarios siempre que sea posible. Una instrucción (o transacción) fallida no deja efectos parciales del programa de esa ruta de fallo.
Deserializa una vez en la parte superior, luego match a los manejadores. Pasa program_id a las rutas de PDA y propietario. Mapea los errores del dominio a códigos estables ProgramError::Custom para los clientes.
Los datos de instrucción son el formato de transmisión entre los constructores fuera de la cadena y el código en la cadena. Un patrón común es un enum de Borsh (primer byte índice de variante, luego campos):
No reordenes las variantes en vivo. Limita las cadenas y vectores ilimitados. Agrega un byte de versión cuando debas romper el diseño. Refleja la misma codificación en TypeScript con @solana/kit 7.0.0 o un pipeline de Codama/schema compartido para que los clientes no puedan desviarse silenciosamente. Ver Serialización/Deserialización de Datos de Instrucción.
La seguridad nativa es una lista de verificación explícita en cada ruta:
Verificación
Patrón
Firmante
account.is_signer
Escribible
account.is_writable
Propietario es tu programa
account.owner == program_id
Propietario es System/Token/etc.
igualdad en el ID de programa esperado
Identidad PDA
find_program_address + bump almacenado
Relaciones
igualdad de claves; campos de mint/autoridad en los datos
Sysvars
Sysvar::get() o claves conocidas, no falsificaciones falsificables
Ejecuta verificaciones de indicadores baratas antes de CPI o deserialización pesada. Documenta el orden de las cuentas; consume con next_account_info. Para las cuentas de tokens, el propietario es el programa Token; la autoridad y el mint viven dentro de los datos.
Los programas crean cuentas mediante CPI al System Program (create_account o allocate/assign relacionados). El pagador financia los lamports exentos de alquiler; space dimensiona el buffer; owner se convierte en tu program_id para el estado del programa.
Las PDAs no tienen clave privada. Usa invoke_signed con semillas y bump exactos:
let rent = Rent::get()?;let lamports = rent.minimum_balance(State::LEN);invoke_signed( &system_instruction::create_account( payer.key, pda.key, lamports, State::LEN as u64, program_id, ), &[payer.clone(), pda.clone(), system_program.clone()], &[&[b"vault", owner.key.as_ref(), &[bump]]],)?;// inicializar datos después de la creación
Valida el ID del System Program, las suposiciones de cuenta vacía/nueva y la coincidencia de semillas. Las rutas de reinicialización y "la cuenta ya existe" son clases clásicas de explotación. Flujo completo: Creación de Cuentas a través de CPI.
msg! y las utilidades relacionadas escriben registros de transacciones para simulación, exploradores e indexadores. El costo escala con el volumen y el tamaño de la cadena. Usa puntos de referencia estructurados en desarrollo y registros de producción dispersos. Prefiere errores personalizados tipados para fallos esperados. Ver Emisión de Registros.
Nativo todavía necesita una historia de cliente: constructores escritos a mano, un crate compartido o herramientas de Codama/IDL. Las pruebas deben afirmar fallos de validación (propietario incorrecto, firmante faltante, PDA incorrecta), no solo rutas felices. Las pruebas unitarias estilo LiteSVM y los validadores locales bajo Solana CLI 3.0.10 cubren la velocidad de la lógica frente a los bucles de despliegue RPC completos.
Prefiere nativo cuando al menos dos se cumplen: la CU de instrucción es crítica para el negocio; el diseño de la cuenta lucha contra Anchor; el tamaño binario está restringido; los auditores exigen validación totalmente explícita; o despliegas una primitiva en la que otros programas harán CPI.
No elijas nativo solo porque "Anchor es lento" sin perfilar. La contención, RPC y los algoritmos a menudo dominan la sobrecarga de macros.
Forma común: Anchor para administración e instrucciones de baja frecuencia; nativo para el bucle cerrado (paso de coincidencia, tick del juego, liquidación). Compón con CPI y diseños compartidos. Documenta qué ID de programa posee qué estado.
Para cada instrucción pregunta: ¿quién debe firmar?; ¿qué cuentas se escriben y con qué propietario?; ¿se puede sustituir una PDA, mint o cuenta de token diferente?; ¿es initialize invocable de nuevo en datos no vacíos?; ¿son las semillas/bumps canónicas?; ¿se liberan los préstamos antes de la CPI?; ¿coincide el diseño de la instrucción con los clientes publicados?
Fija Agave, CLI y solana-program como dependencias de la aplicación. Documenta los códigos de error personalizados para los consumidores de @solana/kit. Trata los cambios de diseño como migraciones con puertas de versión.
Concepto erróneo: Nativo omite la seguridad porque el runtime ya verificó las cuentas.
El runtime verifica las firmas y los metadatos de escritura para las cuentas listadas. No conoce las semillas de tu bóveda, las vinculaciones de mint o la política de administración.
Concepto erróneo: is_writable significa que la cuenta es segura para tratarla como tu estado.
Escribible solo significa que la transacción permitió la mutación. Aún así, verifica el propietario, la identidad y los discriminadores.
Concepto erróneo: Propietario igual a Token Program es suficiente para "la cuenta de token del usuario".
La autoridad, el mint y la cantidad viven en los datos. Valida los campos (y a menudo la derivación de ATA) para la acción económica.
Concepto erróneo: Los enums de instrucción se pueden reordenar si Rust todavía compila.
Los índices de variante son el formato de transmisión. Reordenar rompe los clientes antiguos y puede mapear opcodes antiguos a manejadores nuevos.
Concepto erróneo: Crear una PDA es simplemente create_account sin semillas.
Las acciones autorizadas por PDA necesitan invoke_signed con las semillas que el runtime espera.
Concepto erróneo: Los registros de msg! son gratuitos y observables.
Los registros cuestan CU y aumentan el tamaño de los metadatos. No son un sustituto de los errores o las métricas fuera de la cadena.
Concepto erróneo: Los programas nativos son automáticamente más seguros que Anchor.
Ambos fallan bajo validación incorrecta. Nativo mueve el predeterminado de "la macro podría omitir" a "el autor podría omitir".
Concepto erróneo: Los programas nativos no necesitan un IDL o esquema de cliente.
Sin una codificación publicada, los constructores divergen. Despliega un esquema, un crate compartido o un pipeline de Codama.
¿Qué es un programa nativo de Solana en una oración?
Un crate SBF solana-program con un punto de entrada que analiza manualmente los datos de instrucción, valida AccountInfos y actualiza el estado propiedad del programa (a menudo con System Program CPI), sin macros de Anchor.
¿Los programas nativos usan un runtime diferente al de los programas Anchor?
No. Ambos son SBF bajo el mismo runtime de Agave. Anchor genera más pegamento; la cadena ve un ID de programa y bytecode de cualquier manera.
¿Qué recibe el punto de entrada?
program_id, un slice de AccountInfo para las cuentas listadas y un slice de bytes de datos de instrucción.
¿Por qué el orden de las cuentas es parte de la API?
Los clientes construyen una lista paralela de AccountMeta. Los manejadores típicamente consumen cuentas en orden fijo con next_account_info. Documenta ese orden o genéralo a partir de una única fuente de verdad.
¿Qué debo validar que el runtime no hace?
Identidad comercial: qué PDA, mint y autoridad; si initialize ya se ejecutó; relaciones entre cuentas; y política de propietario más allá de la presencia de la firma.
¿Cómo debo estructurar los datos de instrucción?
Prefiere un diseño documentado y versionado (comúnmente un enum de Borsh). Mantén las variantes estables, limita los tamaños dinámicos y refleja el diseño en los clientes.
¿Cómo creo una cuenta propiedad del programa desde código nativo?
CPI system_instruction::create_account con lamports exentos de alquiler, espacio deseado y owner = program_id. Usa invoke_signed cuando la nueva dirección sea una PDA.
¿Cuándo necesito `invoke_signed`?
Siempre que una PDA deba autorizar una acción porque no existe una clave privada Ed25519 para esa dirección.
¿Cómo leo y escribo estado de forma segura?
Verifica el propietario y la longitud, toma prestados los datos, deserializa o usa una vista segura, modifica con aritmética verificada, serializa dentro de la capacidad y libera los préstamos antes de la CPI en la misma cuenta.
¿Son necesarios los registros `msg!`?
No. Úsalos para depuración y puntos de referencia dispersos en producción. Prefiere valores ProgramError claros para fallos esperados.
¿Cómo se comunican los clientes con los programas nativos?
Construye transacciones que listen las cuentas correctas y serialicen los mismos bytes de instrucción que espera el programa, usando @solana/kit 7.0.0 u otros SDKs.
¿Es el nativo siempre menor en CU que Anchor?
Nativo tiene un piso más bajo y más control, pero la CU real depende de las verificaciones, registros, serialización y CPI. Mide las instrucciones críticas.
¿Puedo mezclar Anchor y nativo?
Sí. Los programas separados compuestos con CPI son comunes: Anchor para superficies administrativas, nativo para lógica central sensible al rendimiento.
¿Cuál es el mayor error de seguridad en los programas nativos?
Validación incompleta: firmante faltante, propietario incorrecto, sustitución de PDA o reinicialización en cuenta existente. Trata cada instrucción como entrada hostil.
¿A dónde debo ir ahora en esta sección?
Comienza con Fundamentos de Programas Nativos, luego profundiza en la validación, datos de instrucción, creación de CPI, E/S de cuentas y registros a través de los elementos relacionados a continuación.