Plano de Patrones On-Chain
Los patrones de programas on-chain son diseños reutilizables sobre cómo un programa de Solana crea estado, decide quién puede cambiarlo, mueve valor, avanza el ciclo de vida y reacciona al tiempo. No son guías de estilo opcionales. En Solana, cada campo duradero vive en una cuenta propiedad del programa que debes pasar explícitamente, por lo que las reglas de seguridad y del producto aparecen como diseños de cuentas, verificaciones de firmantes, protecciones de inicialización y tablas de transición en lugar de middleware oculto del lado del servidor.
Conceptos Básicos de Patrones de Programas es la entrada práctica; Control de Acceso y Autoridades, Patrones de Inicialización de Cuentas, Patrones de Tarifas y Tesorerías, Máquinas de Estado On-Chain y Tiempo y Reloj se centran en un mecanismo cada uno. Esta página se sitúa debajo: cómo esos patrones forman un plano que puedes aplicar al diseñar o revisar cualquier programa.
Resumen
- Los programas seguros de Solana combinan control de acceso, inicialización segura, enrutamiento de valor (tarifas y tesorerías), estado explícito del ciclo de vida y reglas de tiempo respaldadas por el reloj para que cada mutación tenga un actor conocido, una forma de cuenta conocida y un siguiente paso legal conocido.
- Por Qué Importa: El tiempo de ejecución impone la propiedad y los bloqueos; no impone tu producto. Los patrones codifican "quién, cuándo, cuánto y en qué orden" para que los clientes no puedan inventar transiciones, reinicializar cuentas o agotar tesorerías a través de verificaciones faltantes.
- Conceptos Clave: autoridad vs propietario, verificaciones de firmante y rol, protecciones de inicialización / reinicialización, discriminadores, PDAs de tesorería y bps de tarifas, enumeraciones de estado y tablas de transición, sysvar Clock, ranuras vs marcas de tiempo unix.
- Cuándo Usar: Diseñar un nuevo programa, revisar superficies de instrucciones, codificar flujos de depósito en garantía o de pedidos, agregar tarifas de protocolo, rotar administradores o explicar por qué "solo un booleano" no es un ciclo de vida.
- Limitaciones / Compensaciones: Los patrones agregan campos de cuenta, instrucciones y CU; los administradores excesivamente centralizados intercambian flexibilidad por confianza; las reglas de tiempo necesitan margen para la deriva del reloj; las tarifas y las máquinas de estado deben mantenerse lo suficientemente simples como para ser auditadas.
- Temas Relacionados: control de acceso, inicialización de cuentas, tarifas y tesorerías, máquinas de estado, tiempo y reloj, conceptos básicos de patrones de programas.
Fundamentos
Un programa de Solana es un ejecutable sin estado. Las reglas duraderas viven en cuentas: configuración, registros de usuario, bóvedas, pedidos y tesorerías. Los patrones responden a cinco preguntas recurrentes que cada instrucción debe resolver antes de mutar cualquier cosa:
- ¿Quién está permitido? Control de acceso y autoridades.
- ¿Esta cuenta ya significa algo? Inicialización y protección contra reinicialización.
- ¿A dónde va el valor? Tarifas, tesorerías y rutas de custodia.
- ¿En qué fase está este objeto? Máquinas de estado y transiciones legales.
- ¿Es demasiado pronto o demasiado tarde? Reloj, ranuras y plazos.
Esas preguntas se componen. Una instrucción de llenado podría requerir al creador o al tomador como firmante, una cuenta de pedido que fue inicializada con el discriminador correcto, un estado de Abierto, una fecha límite no expirada de Clock y una tarifa dividida en un ATA de tesorería, todo antes de cualquier CPI de token.
Llega la instrucción
|
v
+------------------+
| Control de acceso| <- ¿firmante? ¿rol? ¿autoridad PDA?
+--------+---------+
|
v
+------------------+
| Forma de cuenta | <- ¿inicializada? ¿discriminador correcto? ¿propietario correcto?
+--------+---------+
|
v
+------------------+
| Ciclo de vida/tiempo| <- ¿estado legal? ¿fecha límite? ¿bandera de pausa?
+--------+---------+
|
v
+------------------+
| Movimiento de valor| <- matemáticas de tarifas, tesorería, CPI de bóveda
+--------+---------+
|
v
Confirma nuevos bytes (y eventos)Propietario en los metadatos de la cuenta es siempre un ID de programa: solo ese programa puede reescribir data. Autoridad suele ser una clave pública de aplicación (o PDA) almacenada dentro de data que tu programa compara con un firmante de transacción (o autoriza a través de invoke_signed). Confundir los dos es la raíz de muchos errores de control de acceso: el tiempo de ejecución no sabe que tu campo de administrador existe hasta que lo verificas.
Inicialización es el momento en que un búfer vacío (o recién asignado) se convierte en estado tipificado. Hasta que un discriminador y los campos se escriben bajo una protección de reinicialización, nada de lo que sigue (depósitos, roles, tarifas) es confiable. Tarifas y tesorerías son solo patrones de enrutamiento de valor: matemáticas de puntos base verificadas y una cuenta de destino cuya menta y propietario verificas. Máquinas de estado reemplazan los booleanos ad hoc con una enumeración y una tabla de transición para que los objetos completados o cancelados no puedan reutilizarse. El tiempo proviene de la sysvar Clock (slot, epoch, unix_timestamp), no de un argumento arbitrario del cliente que nunca revalidas on-chain.
Mecánicas e Interacciones
Control de acceso y autoridades
Cada ruta privilegiada necesita una verificación concreta: la clave pública esperada está presente, es un firmante cuando una billetera debe aprobar, y coincide con el campo de rol almacenado (administrador, operador, propietario). La separación de roles mantiene los poderes de pausa y configuración distintos de los derechos de depósito del usuario. El diseño de administrador de producción favorece multisig o gobernanza como clave pública de administrador, transferencia de administrador en dos pasos (establecer pendiente, luego aceptar) y la superficie de administrador más pequeña posible para que una clave comprometida no pueda agotar las bóvedas de los usuarios.
Las autoridades controladas por programas usan PDAs y invoke_signed: el programa demuestra semillas, no una clave privada. La custodia del usuario de su estado personal suele ser "el campo propietario debe ser igual al firmante". La custodia del protocolo de las bóvedas es "la PDA debe firmar la transferencia del token después de que pasen las reglas comerciales".
Patrones de inicialización de cuentas
La inicialización es una puerta de un solo sentido. Flujo típico: el Programa del Sistema (o init de Anchor) asigna espacio y asigna tu programa como propietario; tu lógica escribe un discriminador y los campos predeterminados; las instrucciones posteriores rechazan las cuentas que ya muestran ese discriminador o que fallan las protecciones de "ya inicializado". Prefiere crear-then-init explícito cuando la UX lo permita; trata init_if_needed como de alto riesgo a menos que la verificación inicializada sea infalible.
El orden importa para la seguridad: inicializa la estructura antes de que la cuenta contenga valor significativo. Un ataque de reinicialización reescribe los punteros de autoridad o de bóveda después de que los fondos llegaron. Los discriminadores únicos por tipo de cuenta evitan la confusión de tipos (pasar una configuración donde se espera una bóveda).
Patrones de tarifas y tesorerías
Las tarifas del protocolo descuentan puntos base (bps) de una cantidad, luego enrutan la tarifa a una tesorería (a menudo una ATA propiedad de PDA por menta) y el resto al destinatario. Toda la aritmética debe usar operaciones verificadas; limita los fee_bps configurables en set-config (por ejemplo, nunca por encima de un máximo documentado); documenta el redondeo (hacia el protocolo o el usuario). Verifica la menta y el propietario de la tesorería en cada ruta de tarifas para que las tarifas no terminen en una ATA incorrecta.
cantidad
|
+-- tarifa = cantidad * bps / 10_000 --> tesorería
|
+-- neto = cantidad - tarifa --> usuario / contraparteToma tarifas en acciones económicas exitosas (o en entradas claramente definidas), no en rutas medias fallidas que dejan a los usuarios cobrados sin el cambio de estado prometido.
Máquinas de estado on-chain
Los objetos del ciclo de vida (pedidos, depósitos en garantía, subastas, propuestas) almacenan una enumeración de estado. Cada instrucción implementa una transición o consulta un mapa de transición compartido: solo Abierto -> Lleno o Abierto -> Cancelado, nunca Lleno -> Abierto a menos que diseñes intencionalmente una ruta de administrador rara. Los estados terminales aceptan el cierre (o limpieza similar) y rechazan las mutaciones comerciales. Emite eventos en las transiciones para que los indexadores y clientes no adivinen a partir de campos parciales.
Centraliza las verificaciones de estado en ayudantes para que una instrucción olvidada no pueda eludir la máquina. Mantén la enumeración pequeña; reserva valores o campos de versión si esperas estados futuros.
Tiempo y reloj
Lee Clock::get() (o la cuenta sysvar Clock) para vincular el tiempo. Usa unix_timestamp para plazos humanos (acantilados de adquisición de derechos, expiración de depósitos en garantía); usa slot cuando te importe el progreso de la cadena en relación con el cronograma del líder; usa epoch para reglas de estilo de período de participación. Las marcas de tiempo de los validadores tienen una deriva limitada, por lo que los diseños de producción usan períodos de gracia en lugar de precisión de una sola ranura para la experiencia de usuario del reloj de pared. Nunca confíes solo en una "hora actual" proporcionada por el cliente; almacena las fechas límite al inicializar y compáralas con Clock en cada instrucción con acceso restringido.
Cómo se apilan los patrones en un solo flujo
La liberación de depósito en garantía es una instancia completa del plano:
- Inicialización creó el depósito en garantía con discriminador, partes, cantidad, fecha límite, estado
Financiado. - Control de acceso requiere al vendedor o comprador (o reglas de PDA) como firmante dependiendo de la liberación vs. la cancelación.
- Máquina de estado solo permite
Financiado -> LiberadooFinanciado -> Cancelado/Expirado. - Reloj rechaza la liberación después de la fecha límite si la cancelación al expirar es la regla (o viceversa para bloqueos de tiempo).
- Tarifas (si las hay) se dividen al liberar; se verifican la tesorería y las cuentas de token; la PDA de la bóveda firma la transferencia.
Si falta alguna capa, el resto es teatro: una fórmula de tarifas perfecta no ayuda si la reinicialización restablece el campo de autoridad.
Consideraciones Avanzadas y Aplicaciones
Los patrones escalan desde un programa contador hasta un lugar de DeFi; la diferencia es cuán estrictamente los compones y cómo intercambias el poder administrativo por la inmutabilidad.
| Clúster de patrones | Fortaleza | Debilidad | Mejor ajuste |
|---|---|---|---|
| Control de acceso / autoridades | Roles claros; administrador rotatorio; custodia de PDA | El administrador demasiado amplio es un sumidero de confianza | Configuración, pausa, bóvedas, autoridad de menta |
| Protecciones de inicialización / reinicialización | Estado tipificado estable; detiene la sustitución de cuentas | Instrucciones adicionales y disciplina de alquiler | Cualquier cuenta propiedad del programa que contenga valor o roles |
| Tarifas / tesorería | Ingresos transparentes del protocolo; rastro de auditoría on-chain | Errores matemáticos y bps ilimitados perjudican a los usuarios | Mercados, mentas con tarifas, retiros |
| Máquinas de estado | Ciclo de vida auditable; bloquea la repetición de trabajo hecho | Estados demasiado finos hinchan la lógica | Depósitos en garantía, pedidos, subastas, gobernanza |
| Tiempo / reloj | Plazos aplicables sin cronograma fuera de cadena | La deriva y la variación de ranuras necesitan margen | Adquisición de derechos, expiración, períodos de enfriamiento, épocas |
Las banderas de pausa son una máquina de estado estrecha sobre todo el protocolo: el administrador establece pausado, las instrucciones del usuario fallan mientras es verdadero, la reanudación es un privilegio separado. Combina la pausa con la separación de roles para que una clave de operador activa tampoco pueda cambiar los destinatarios de las tarifas.
El diseño seguro para actualizaciones interactúa con estos patrones: los campos de versión y el espacio reservado le permiten agregar roles o estados sin romper los discriminadores; vea el diseño de datos orientado a actualizaciones en esta sección cuando planifique cuentas de larga duración. Las especializaciones de bóveda y depósito en garantía son tarifas + autoridad PDA + máquina de estado + reloj aplicados a la custodia.
Para las revisiones de diseño, enumera cada instrucción y completa: firmantes y roles requeridos, suposiciones de inicialización, estados antes/después, predicados del reloj y sumideros de valor. Los huecos en esa matriz son la superficie de explotación habitual.
Conceptos Erróneos Comunes
- "El tiempo de ejecución aplica mi rol de administrador." Aplica la propiedad del programa sobre los datos. Los roles de administrador y operador solo existen si tu programa verifica las claves públicas almacenadas y los firmantes en cada instrucción privilegiada.
- "Inicializar una vez en el despliegue es suficiente para todas las cuentas." El despliegue inicializa el binario del programa, no las bóvedas o pedidos de los usuarios. Cada cuenta propiedad del programa necesita su propia ruta de creación/inicialización y protección contra reinicialización.
- "Las tarifas pueden usar matemáticas enteras ordinarias." Multiplicaciones/divisiones sin verificar y bps sin límite son vectores de drenaje clásicos. Usa matemáticas verificadas y valida los límites de las tarifas cuando cambie la configuración.
- "Unos pocos booleanos son lo mismo que una máquina de estado." Los indicadores independientes permiten combinaciones ilegales (ambos
llenoycanceladoverdaderos). Una enumeración más verificaciones de transición hace que los estados ilegales no sean representables o sean rechazados. - "Las marcas de tiempo del cliente están bien si la interfaz de usuario es honesta." Cualquiera puede construir una transacción. Las fechas límite y los períodos de enfriamiento deben compararse con Clock (o slot) on-chain.
- "Una única clave de administrador global es más simple y, por lo tanto, más segura." Es más simple hasta la pérdida o el compromiso de la clave. Multisig, transferencia en dos pasos y privilegios mínimos reducen el radio de explosión.
Preguntas Frecuentes
¿Qué es un "patrón de programa on-chain" en esta sección?
Una forma reutilizable de estructurar cuentas e instrucciones para que un programa de Solana resuelva repetidamente el control de acceso, la inicialización, el enrutamiento de valor, el ciclo de vida o el tiempo de una manera auditable y segura.
¿Cómo difieren propietario y autoridad?
Propietario es el ID del programa en los metadatos de la cuenta (quién puede escribir data). Autoridad es generalmente una clave pública de nivel de aplicación o PDA almacenada en data que tu programa verifica contra un firmante o usa con invoke_signed.
¿Por qué se trata la inicialización como un límite de seguridad?
Antes de la inicialización, el búfer no tiene tipo o tiene forma de atacante. Sin discriminadores y protecciones de reinicialización, un atacante puede restablecer autoridades o confundir tipos de cuenta después de que se haya depositado valor.
¿Cuándo debo usar init_if_needed?
Solo cuando la verificación inicializada sea estricta (discriminador o equivalente) y la reinicialización sea imposible. Prefiere init simple o crear por separado cuando la cuenta deba existir exactamente una vez bajo semillas conocidas.
¿Cómo deben implementarse las tarifas del protocolo?
Calcula la tarifa con matemáticas verificadas a partir de bps limitados, verifica la cuenta de tesorería (menta, propietario, semillas de PDA), transfiere la tarifa y luego el neto (o documenta el orden), y nunca permitas que la configuración establezca tarifas ilimitadas.
¿Dónde debe vivir una tesorería?
Típicamente una cuenta de token dedicada controlada por PDA (o PDA SOL) por menta o clase de activo, separada de las bóvedas de los usuarios, para que los retiros y la acumulación de tarifas se mantengan auditables y controlados por roles.
¿Qué pertenece a una máquina de estado on-chain?
Cualquier ciclo de vida de varios pasos donde algunas acciones son ilegales después de otras: abrir/llenar/cancelar, financiar/liberar/reembolsar, proponer/votar/ejecutar. Codifica el estado, valida las transiciones, trata los terminales como cerrados para operaciones comerciales.
¿Debería cada transición ser su propia instrucción?
A menudo sí para claridad y mínimo privilegio (llenar vs. cancelar como manejadores separados). Los ayudantes compartidos aún pueden centralizar la coincidencia de estado para que ninguna ruta omita la tabla.
¿Para qué se utiliza la sysvar Clock?
Vincular el tiempo on-chain: unix_timestamp para plazos del reloj de pared, slot para el progreso discreto de la cadena, epoch para reglas con ámbito de época. Léelo en el programa; no confíes en el "ahora" solo del cliente.
¿Ranuras o marcas de tiempo unix para la expiración del depósito en garantía?
Prefiere marcas de tiempo unix cuando los usuarios razonan en tiempo de calendario; usa ranuras cuando te importe el progreso discreto de la cadena. Permite un pequeño margen de gracia donde la deriva del reloj del validador importa para la experiencia del usuario.
¿Cómo encajan las PDAs en estos patrones?
Las PDAs otorgan a los programas direcciones deterministas y firma sin claves privadas. Potencian las autoridades de bóveda, las cuentas de configuración, las tesorerías de tarifas y las semillas de estado por usuario que los clientes pueden derivar sin conexión.
¿Qué es la transferencia de administrador en dos pasos?
El administrador actual propone un administrador_pendiente; la nueva clave debe firmar una instrucción de aceptación. Eso evita que una errata entregue permanentemente el control a una dirección inutilizable.
¿Puedo omitir las máquinas de estado para una bóveda simple?
Si la bóveda solo tiene saldo y nunca tiene fases, el saldo más la autoridad pueden ser suficientes. Tan pronto como tengas rutas de financiación/liberación/cancelación/expiración, un estado explícito evita acciones contradictorias.
¿Cómo se relacionan estos patrones con las restricciones de Anchor?
Anchor codifica muchos de ellos: restricciones has_one y de firmante para el control de acceso, init / semillas / espacio para la inicialización, y tipos de cuenta para la propiedad. Aún diseñas las reglas del producto; las macros implementan las verificaciones que especificas.
¿Qué debe incluir una lista de verificación de revisión de diseño?
Para cada instrucción: firmantes y roles requeridos, suposiciones de inicialización, transiciones de estado, predicados del reloj, destinos de tarifas y tesorerías, y qué cuentas deben ser PDAs con semillas verificadas.
Relacionados
- Conceptos Básicos de Patrones de Programas - ejemplos prácticos de patrones y bloques de construcción
- Control de Acceso y Autoridades - roles, verificaciones de firmantes, rotación de administradores
- Patrones de Inicialización de Cuentas - inicialización, protecciones de reinicialización, discriminadores
- Patrones de Tarifas y Tesorerías - tarifas de bps, tesorerías, matemáticas verificadas
- Máquinas de Estado On-Chain - enumeraciones de estado y transiciones legales
- Tiempo y Reloj - sysvar Clock, ranuras, plazos, períodos de enfriamiento
Versiones de Stack: Esta página fue escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1 y @solana/kit 7.0.0.