Creación de Transacciones en Python
Las transacciones de Solana agrupan una o más instrucciones con un recent blockhash y un conjunto de firmantes. En Python, compones instrucciones con solders, adjuntas firmantes, simulas contra el RPC de Agave 4.1.1 y luego envías con solana-py.
Receta
Tarjeta de referencia rápida - lista para copiar y pegar.
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.hash import Hash
from solders.message import Message
from solders.transaction import VersionedTransaction
from solders.system_program import TransferParams, transfer
from solana.rpc.api import Client
client = Client("https://api.devnet.solana.com")
payer = Keypair()
blockhash = Hash.from_string(str(client.get_latest_blockhash().value.blockhash))
ix = transfer(TransferParams(from_pubkey=payer.pubkey(), to_pubkey=Pubkey.default(), lamports=1000))
msg = Message.new_with_blockhash([ix], payer.pubkey(), blockhash)
tx = VersionedTransaction(msg, [payer])Cuándo usar esto:
- Servicios backend que retransmiten transacciones firmadas por el usuario o custodiadas.
- Bots que ensamblan lotes atómicos de múltiples instrucciones.
- Scripts que replican las transferencias de Solana CLI 3.0.10 con lógica personalizada.
- Pipelines que añaden instrucciones de presupuesto de cómputo y tarifas de prioridad.
Ejemplo Funcional
import asyncio
from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.hash import Hash
from solders.message import Message
from solders.transaction import VersionedTransaction
from solders.system_program import TransferParams, transfer
from solana.rpc.async_api import AsyncClient
from solana.rpc.types import TxOpts
RPC = "https://api.devnet.solana.com"
async def send_with_budget(dest: Pubkey, lamports: int, micro_lamports: int):
payer = Keypair()
async with AsyncClient(RPC) as client:
bh = await client.get_latest_blockhash()
blockhash = Hash.from_string(str(bh.value.blockhash))
ixs = [
set_compute_unit_limit(50_000),
set_compute_unit_price(micro_lamports),
transfer(TransferParams(from_pubkey=payer.pubkey(), to_pubkey=dest, lamports=lamports)),
]
msg = Message.new_with_blockhash(ixs, payer.pubkey(), blockhash)
tx = VersionedTransaction(msg, [payer])
sim = await client.simulate_transaction(tx)
if sim.value.err:
raise RuntimeError(f"simulation failed: {sim.value.err} logs={sim.value.logs}")
resp = await client.send_transaction(
tx,
opts=TxOpts(skip_preflight=True, preflight_commitment="confirmed"),
)
await client.confirm_transaction(resp.value, commitment="confirmed")
return resp.value
asyncio.run(send_with_budget(Pubkey.default(), 10_000, 5_000))Lo que esto demuestra:
- Las instrucciones de Compute Budget preceden a las instrucciones del programa.
- El blockhash se obtiene fresco del RPC en cada intento de envío.
- La simulación valida las cuentas necesarias antes de la presentación.
- El sondeo de confirmación espera el compromiso
confirmed.
Análisis Profundo
Cómo Funciona
- Un mensaje lista las claves de las cuentas, el recent blockhash y las instrucciones compiladas.
- Los firmantes deben cubrir todas las cuentas firmantes escribibles en la cabecera del mensaje.
- Las transacciones versionadas admiten tablas de búsqueda de direcciones (v0) para grandes listas de cuentas.
- El RPC
sendTransactiontransmite al líder; la confirmación es asíncrona.
Lista de Verificación de Ensamblaje de Transacciones
| Paso | API | Notas |
|---|---|---|
| 1. Construir instrucciones | solders.*_program helpers | El orden importa para los efectos secundarios de CPI |
| 2. Obtener blockhash | get_latest_blockhash | Actualizar en caso de reintento |
| 3. Crear mensaje | Message.new_with_blockhash | El pagador de tarifas es el primer firmante |
| 4. Firmar | VersionedTransaction(msg, signers) | Todos los firmantes requeridos presentes |
| 5. Simular | simulate_transaction | Inspeccionar logs en caso de error |
| 6. Enviar | send_transaction | Ajustar skip_preflight según el entorno |
Notas de Python
# Firma parcial para multisig o firmantes remotos
tx = VersionedTransaction(msg, []) # sin firmar
tx.sign([local_signer], blockhash)
# enviar bytes al cosigner, luego fusionar firmas según tu modelo de custodia
# Leer estado de confirmación
status = client.get_signature_statuses([sig])Errores Comunes
- Firmante faltante - la simulación devuelve
MissingRequiredSignature. Solución: auditar las banderasAccountMeta.is_signer. - Blockhash expirado -
BlockhashNotFound. Solución: reconstruir el mensaje con un nuevo blockhash. - Fondos insuficientes para alquiler - la creación de cuentas requiere lamports más allá del monto de la transferencia. Solución: simular y financiar al pagador.
- Orden de instrucciones - el presupuesto de cómputo debe ser el primero. Solución: anteponer las instrucciones de presupuesto.
- Asumir finalidad síncrona -
send_transactiondevuelve una firma, no una confirmación. Solución: consultarconfirm_transaction.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
Solana CLI transfer | Operaciones manuales | Estrategias automatizadas |
@solana/kit en ruta API | Billetera del navegador + cosigner del servidor | Pila puramente Python |
| RPC de paquete Jito | Bots de MEV sensibles a la latencia | Transferencias simples |
| Servicio de firma remota | Exchanges de custodia | Pruebas en devnet en solitario |
Preguntas Frecuentes
¿Transacción Legacy vs VersionedTransaction?
Prefiere VersionedTransaction para código Python nuevo; se alinea con las transacciones v0 en Agave 4.x.
¿Cuántas instrucciones por tx?
El límite práctico es el tamaño de la transacción (aprox. 1232 bytes) y el presupuesto de CU - simula para validar.
¿Puedo mezclar instrucciones de SPL Token?
Sí - importa los helpers de SPL o construye Instruction manualmente con el ID del programa Token.
¿Cómo establecer un pagador de tarifas diferente del firmante?
Pasa la clave pública del pagador de tarifas a Message.new_with_blockhash e incluye su firma.
¿Reintentos en caso de fallo?
Actualiza el blockhash, opcionalmente aumenta set_compute_unit_price, usa retroceso exponencial.
¿Transacciones con nonce duradero?
Soportado pero raro en bots - úsalo cuando el cambio de blockhash sea problemático.
¿Cómo depurar errores del programa?
Lee los logs de simulate_transaction - coinciden con la salida msg! en la cadena.
¿Seguridad en Mainnet?
Ejecuta en seco en devnet, luego habilita límites de gasto en billeteras calientes.
¿Soporte ALT en Python?
Usa las APIs de mensajes v0 cuando la lista de cuentas exceda los límites heredados - consulta la documentación ALT del RPC.
¿Creación de transacciones con AnchorPy?
AnchorPy puede crear transacciones a partir de IDL - todavía usa solders internamente.
Relacionado
- solders y solana-py - descripción general del SDK
- Tarifas de Prioridad - mercado de tarifas
- Simulación de Transacciones - teoría de simulación
- Trading y Bots - automatización
Versiones de la Pila: Esta página fue escrita 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.